Compare commits

..

1121 Commits

Author SHA1 Message Date
valitovgaziz 17b194dd30 fix: nginx healthcheck (wget / instead of curl /health); add backup healthcheck
Deploy / deploy (push) Failing after 2m42s
2026-06-12 12:55:13 +05:00
valitovgaziz e1807167d2 fix(backup): mount rclone.conf to correct path 2026-06-12 12:51:18 +05:00
valitovgaziz 8645342666 fix(backup): use app-network instead of internal for DB access 2026-06-12 12:50:26 +05:00
valitovgaziz 5e4d78b83d add yandex token 2026-06-12 12:49:19 +05:00
Gaziz Valitov ef84eb9a9d fix(certbot): crond -f instead of -b; add rclone.conf 2026-06-12 12:33:02 +05:00
valitovgaziz 3688abb259 fix: nginx entrypoint - use sed instead of grep -oP for Alpine compat 2026-06-12 12:32:06 +05:00
valitovgaziz 8e766b540e feat: CI/CD, per-domain HTTPS, backup, config generator
- sites.yml — единый источник истины для всех сайтов
- generate-configs.sh — генератор nginx конфигов, certbot domains.txt, .env
- nginx: per-domain HTTPS (вместо all-or-nothing switch-config)
- certbot: единый renew-all.sh, динамический init (без 5 дублирующих скриптов)
- backup: контейнер с pg_dump + rclone (Яндекс.Диск), ежедневно в 3AM
- Gitea + Gitea Runner в docker-compose (self-hosted Git + CI/CD)
- .gitea/workflows/deploy.yml — CI/CD pipeline: push → авто-деплой
- Makefile: generate-configs, reconfig, deploy, backup, restore, gitea, help
2026-06-12 12:22:19 +05:00
valitovgaziz abcb327278 docs: add server info and fix easySite paths in AGENTS.md 2026-06-12 11:52:21 +05:00
valitovgaziz 5d22544df1 docs(nginx): remove outdated serv_spa references
Updated the domains table to reflect that yalarba.ru is now
Nuxt 4 SSR (not static Vue SPA), and the old static path
/usr/share/nginx/yalarba/html no longer exists.
2026-06-12 11:27:28 +05:00
valitovgaziz 0898315910 remove legacy serv_spa (yalarba Vue SPA)
- Deleted main_dc/yalarba/serv_spa/ directory
- No docker-compose.yml changes needed (service was already unused)
- Updated docs references to point to yalarba-nuxt
- docker-compose.yml and nginx configs had no references to serv_spa
2026-06-12 11:26:18 +05:00
valitovgaziz eee067f0ca fix: track Dockerfile and .dockerignore in easySite
.gitignore was ignoring these files, causing them to be missing
on fresh checkout (e.g. after git pull on server), which broke
the Docker build. Removed the ignore entries so the files are
tracked by git.
2026-06-12 11:19:33 +05:00
valitovgaziz 2941b14b38 flatten easySite directory: remove extra easySite/easySite nesting
- Moved contents of main_dc/yalarba/easySite/easySite/ up to easySite/
- Updated docker-compose.yml build context path
- Deleted empty nested easySite/ directory
2026-06-12 11:16:15 +05:00
valitovgaziz 888bb2d87b Fix import alias conflict: gormpg + migratepg 2026-06-12 10:59:39 +05:00
valitovgaziz 029812c6a4 Restructure Dockerfiles: copy source before go mod tidy 2026-06-12 10:57:50 +05:00
valitovgaziz 6a60d67b29 Fix Dockerfile: use go mod tidy instead of go mod download 2026-06-12 10:56:45 +05:00
valitovgaziz b0350abfbe DB optimization: pool, golang-migrate, consolidate to single Postgres
- Fix DB_NAME=db_yal -> mydb in api_yal .env
- Add connection pool (MaxOpenConns 25, MaxIdleConns 10, ConnMaxLifetime 30m)
- Replace GORM AutoMigrate with golang-migrate in api_yal and api_bb
- Create embedded SQL migrations for both APIs
- Add DB_SCHEMA support to api_bb config
- Consolidate to single Postgres: db_bb -> schema 'bb' on db container
- Remove db_bb service, bb-network, db_bb volume from compose
- Remove api_tp targets from Makefile
- Clean up old migrate.go
2026-06-12 10:47:41 +05:00
valitovgaziz ec83b97c25 Remove api_tp from docker-compose.yml (service already deleted) 2026-06-12 10:20:13 +05:00
valitovgaziz 86b8968dce add all command for easysite 2026-06-12 10:18:45 +05:00
valitovgaziz 90a96b4125 Migrate easysite from api_es to api_yal
- Remove api_es service, Dockerfile, all Go source files
- Remove api_es from docker-compose.yml, nginx-ssl.conf, .env, Makefile
- Replace nginx /api/ proxy with /api/v1/ → api_yal:8787
- Add amenity/upload domains, AuthResponse, GET /auth/me, GET /objects/my to api_yal
- Rewrite easysite frontend: types, composables, and all 5 pages to use api_yal DTOs
- Wire nuxt.config public.apiBase, add useObjects CRUD composable
- Update docs references from api_es to api_yal
2026-06-12 10:14:38 +05:00
valitovgaziz 64295b689b docs: add Windows test run commands to README.md 2026-06-12 08:51:16 +05:00
valitovgaziz 75198ed00f docs: add integration test run instructions to README.md
- Added section 8 'Тестирование' with run instructions, structure, features, and diagnostics
- Also includes test file route path adjustments and import reordering
2026-06-12 08:46:20 +05:00
valitovgaziz 01e8226c2b Add integration test suite with in-memory SQLite, mock repos, and test server
- Add test_server.go with chi-based router, shared in-memory SQLite DB, mock repositories
- Add mock_object_repository.go and mock_appeal_repository.go for lightweight testing
- Add setup.go with TestConfig/TestUser helpers, HTTP request builder, and fixtures
- Add go-sqlite3 dependency for in-memory test database
- Rewrite all 7 integration test suites (account, appeal, auth, comment, feedback, object, rating)
  using the new test infrastructure
2026-06-12 08:42:04 +05:00
valitovgaziz 4d5090d76c moove font to fonts directory 2026-06-12 02:49:24 +05:00
valitovgaziz 02c6cb680b fix: align API response shapes (items instead of data), add fallbacks to prevent .length crash 2026-06-12 02:31:37 +05:00
valitovgaziz 86f37dde2d add font Bellaboo.woff and change fonts.ccs 2026-06-12 01:57:32 +05:00
valitovgaziz 9c793bad1b fix: align frontend types and forms with api_yal backend (name→first_name+last_name, token→access_token) 2026-06-12 01:41:23 +05:00
valitovgaziz ba7b757541 fix(nginx): preserve /api/v1 prefix when proxying to api_yal, add favicon 2026-06-12 01:23:06 +05:00
valitovgaziz edb7eabd18 On branch main
modified:   main_dc/yalarba/yalarba-nuxt/package-lock.json
	new file:   opencode.json
chagne temperature for opencode into this project
2026-06-12 01:08:14 +05:00
valitovgaziz d8349a0936 feat: add yalarba-nuxt to infra, wire fonts, switch nginx from static SPA to SSR proxy 2026-06-12 00:37:49 +05:00
valitovgaziz 60867af69c feat: create Nuxt 4 SPA for yalarba.ru (yalarba-nuxt) 2026-06-12 00:29:34 +05:00
valitovgaziz 35ba568d97 On branch main
new file:   main_dc/README.md
	modified:   main_dc/valitovgaziz/package-lock.json
	modified:   main_dc/valitovgaziz/src/components/TheFooter.vue
fix 2025 to 2026 into valitovgaziz site
2026-06-10 13:02:00 +05:00
valitovgaziz f06968eb46 fix: add healthcheck to valitovgaziz container 2026-06-10 11:32:22 +05:00
valitovgaziz 075f29cde1 feat: add personal photo to valitovgaziz hero section 2026-06-10 11:24:19 +05:00
valitovgaziz e8a655d54c feat: containerize valitovgaziz site, add Dockerfile, nginx proxy, Makefile targets 2026-06-10 11:03:11 +05:00
valitovgaziz 6ba49127aa feat: add Vue 3 personal site for valitovgaziz in main_dc/my_site 2026-06-10 10:47:59 +05:00
valitovgaziz 2084acb078 redesign valitovgaziz.ru personal site with clean & minimal UI
- Rewrite index.html with modern layout: sticky nav, hero, about, projects, timeline, skills badges, contact
- Consolidate 12 fragmented CSS files into one cohesive style.css with CSS custom properties and dark mode
- Consolidate JS into scripts.js (dark toggle + scroll animations), remove exposed telegram bot token
- Update blog.html to match new design
- Add AGENTS.md
2026-06-10 10:01:55 +05:00
valitovgaziz d1e45c7686 On branch main
modified:   main_dc/yalarba/api_yal/cmd/testrunner/main.go
	modified:   main_dc/yalarba/api_yal/cmd/testrunner/runner.go
	modified:   main_dc/yalarba/api_yal/tests/integration/account_test.go
	modified:   main_dc/yalarba/api_yal/tests/integration/appeal_test.go
	modified:   main_dc/yalarba/api_yal/tests/integration/auth_test.go
	modified:   main_dc/yalarba/api_yal/tests/integration/comment_test.go
	modified:   main_dc/yalarba/api_yal/tests/integration/feedback_test.go
	modified:   main_dc/yalarba/api_yal/tests/integration/object_test.go
	modified:   main_dc/yalarba/api_yal/tests/integration/rating_test.go
	deleted:    main_dc/yalarba/api_yal/tests/testutils/client.go
	modified:   main_dc/yalarba/api_yal/tests/testutils/fixtures.go
	modified:   main_dc/yalarba/api_yal/tests/testutils/setup.go
write comments for and into test's functions
2026-06-08 01:58:04 +05:00
valitovgaziz b4574f9df1 On branch main
modified:   main_dc/yalarba/api_yal/go.mod
	modified:   main_dc/yalarba/api_yal/go.sum
go mod tidy
2026-06-08 01:46:10 +05:00
valitovgaziz 8dfe7e8b4a On branch main
new file:   main_dc/yalarba/api_yal/cmd/testrunner/README.md
	new file:   main_dc/yalarba/api_yal/cmd/testrunner/main.go
	new file:   main_dc/yalarba/api_yal/cmd/testrunner/runner.go
	deleted:    main_dc/yalarba/api_yal/test/intergration/auth_integration_test.go
	deleted:    main_dc/yalarba/api_yal/test/intergration/objects_integration_test.go
	deleted:    main_dc/yalarba/api_yal/test/intergration/setup_test.go
	deleted:    main_dc/yalarba/api_yal/test/setup_test.go
	new file:   main_dc/yalarba/api_yal/tests/integration/account_test.go
	new file:   main_dc/yalarba/api_yal/tests/integration/appeal_test.go
	new file:   main_dc/yalarba/api_yal/tests/integration/auth_test.go
	new file:   main_dc/yalarba/api_yal/tests/integration/comment_test.go
	new file:   main_dc/yalarba/api_yal/tests/integration/feedback_test.go
	new file:   main_dc/yalarba/api_yal/tests/integration/object_test.go
	new file:   main_dc/yalarba/api_yal/tests/integration/rating_test.go
	new file:   main_dc/yalarba/api_yal/tests/testutils/client.go
	new file:   main_dc/yalarba/api_yal/tests/testutils/fixtures.go
	new file:   main_dc/yalarba/api_yal/tests/testutils/setup.go
write tests
2026-06-08 01:44:23 +05:00
valitovgaziz bdf3ba2483 On branch main
modified:   main_dc/yalarba/api_yal/documentation/TEST_REST_API_REQUESTS.md
replace base url
2026-06-07 22:33:35 +05:00
valitovgaziz b98d1f65d3 On branch main
modified:   main_dc/yalarba/api_yal/documentation/README.md
date last commit that set base URL into easysite102.ru doc
2026-06-07 22:15:30 +05:00
valitovgaziz 787f90b5cf On branch main
modified:   documentation/README.md
SET new base URL for api_yal
2026-06-07 21:56:14 +05:00
valitovgaziz d2b77d4553 On branch main
modified:   main_dc/nginx/nginx-ssl.conf
	modified:   main_dc/yalarba/api_es/internal/config/config.go
add config into enginx for api_yal REST_API
2026-06-07 21:42:16 +05:00
valitovgaziz eb5b8fbf26 On branch main
new file:   .gitattributes
	modified:   main_dc/yalarba/api_yal/go.mod
	modified:   main_dc/yalarba/api_yal/go.sum
	deleted:    main_dc/yalarba/api_yal/test/e2e/api_test.go
	deleted:    main_dc/yalarba/api_yal/test/fixtures/test_data.go
	deleted:    main_dc/yalarba/api_yal/test/intergration/account_intergration_test.go
	modified:   main_dc/yalarba/api_yal/test/intergration/setup_test.go
	new file:   main_dc/yalarba/api_yal/test/setup_test.go
create gitattributes text=auto chate LF=CRLF=>auto
create test's file's
2026-06-07 21:10:44 +05:00
valitovgaziz 1bb91820d0 On branch main
new file:   main_dc/yalarba/api_yal/test/e2e/api_test.go
	new file:   main_dc/yalarba/api_yal/test/fixtures/test_data.go
	new file:   main_dc/yalarba/api_yal/test/intergration/account_intergration_test.go
	new file:   main_dc/yalarba/api_yal/test/intergration/auth_integration_test.go
	new file:   main_dc/yalarba/api_yal/test/intergration/objects_integration_test.go
	new file:   main_dc/yalarba/api_yal/test/intergration/setup_test.go
add test files
not implemented
2026-05-31 05:12:49 +05:00
valitovgaziz 9dd4b5f067 On branch main
new file:   main_dc/yalarba/api_yal/documentation/TEST_REST_API_REQUESTS.md
add tests docs
2026-05-27 14:09:18 +05:00
valitovgaziz 5c34816359 On branch main
modified:   main_dc/yalarba/api_yal/documentation/README.md
Add the patch numbers
2026-05-21 10:16:18 +05:00
valitovgaziz 5eb2f5220b On branch main
new file:   main_dc/yalarba/api_yal/documentation/README.md
Add documentation for endpoints for now
`1
2026-05-21 10:15:02 +05:00
valitovgaziz 318075d686 On branch main
modified:   internal/domain/appeal/dto.go
	new file:   internal/domain/appeal/handler.go
	modified:   internal/domain/appeal/router.go
	modified:   internal/domain/appeal/service.go
	modified:   internal/models/appeal.go
	modified:   internal/router/router.go
fix bag with no embeded the Base into appeal
2026-05-21 05:04:34 +05:00
valitovgaziz ba2e3b9545 On branch main
modified:   main_dc/yalarba/api_yal/internal/domain/rating/dto.go
	new file:   main_dc/yalarba/api_yal/internal/domain/rating/handler.go
	new file:   main_dc/yalarba/api_yal/internal/domain/rating/router.go
	new file:   main_dc/yalarba/api_yal/internal/domain/rating/service.go
	modified:   main_dc/yalarba/api_yal/internal/router/router.go
add raing domain without test
2026-05-20 13:23:38 +05:00
valitovgaziz 508eb8b981 On branch main
modified:   main_dc/yalarba/api_yal/internal/domain/object/router.go
	modified:   main_dc/yalarba/api_yal/internal/router/router.go
add register router into main router
2026-05-20 13:17:19 +05:00
valitovgaziz cc3d0a8b07 On branch main
modified:   yalarba/api_yal/internal/domain/account/service.go
	modified:   yalarba/api_yal/internal/domain/comment/dto.go
	new file:   yalarba/api_yal/internal/domain/comment/handler.go
	new file:   yalarba/api_yal/internal/domain/comment/router.go
	new file:   yalarba/api_yal/internal/domain/comment/service.go
	modified:   yalarba/api_yal/internal/repository/feedback_repository.go
	new file:   yalarba/api_yal/internal/util/JSON_resp.go
Realize comment domain hole
2026-05-19 18:11:20 +05:00
valitovgaziz 63d486f48d On branch main
modified:   main_dc/yalarba/api_yal/internal/domain/appeal/router.go
	modified:   main_dc/yalarba/api_yal/internal/domain/feetback/dto.go
	modified:   main_dc/yalarba/api_yal/internal/domain/feetback/handler.go
	modified:   main_dc/yalarba/api_yal/internal/domain/feetback/router.go
	modified:   main_dc/yalarba/api_yal/internal/domain/feetback/service.go
	modified:   main_dc/yalarba/api_yal/internal/models/feedback.go
	modified:   main_dc/yalarba/api_yal/internal/repository/comment_repository.go
	modified:   main_dc/yalarba/api_yal/internal/repository/feedback_repository.go
	modified:   main_dc/yalarba/api_yal/internal/repository/feedback_repository_impl.go
	modified:   main_dc/yalarba/api_yal/internal/router/router.go
craete routerRegister, service, hander, dto for feedback
2026-05-19 15:01:57 +05:00
valitovgaziz 42549eb116 last 2026-05-19 14:16:43 +05:00
valitovgaziz 894415e3ac On branch main
modified:   main_dc/yalarba/api_yal/internal/domain/feetback/dto.go
	modified:   main_dc/yalarba/api_yal/internal/domain/feetback/service.go
last
2026-05-19 14:07:06 +05:00
valitovgaziz e4a1fcfd25 On branch main
modified:   main_dc/yalarba/api_yal/internal/domain/feetback/dto.go
	modified:   main_dc/yalarba/api_yal/internal/domain/feetback/handler.go
	modified:   main_dc/yalarba/api_yal/internal/domain/feetback/router.go
	modified:   main_dc/yalarba/api_yal/internal/domain/feetback/service.go
feedback domain is almost ready
2026-05-19 13:19:47 +05:00
valitovgaziz 4e80d525db Merge branch 'main' of github.com:valitovgaziz/tp 2026-05-18 09:37:32 +05:00
valitovgaziz 8d30480bdc On branch main
new file:   main_dc/yalarba/api_yal/internal/domain/appeal/dto.go
	new file:   main_dc/yalarba/api_yal/internal/domain/appeal/router.go
	new file:   main_dc/yalarba/api_yal/internal/domain/appeal/service.go
	new file:   main_dc/yalarba/api_yal/internal/domain/feetback/handler.go
	new file:   main_dc/yalarba/api_yal/internal/domain/feetback/router.go
	new file:   main_dc/yalarba/api_yal/internal/domain/feetback/service.go
try add domains for appeal
2026-05-18 09:36:53 +05:00
Ildar 4cf8543c82 Fix typo in README documentation location 2026-05-04 15:28:58 +05:00
valitovgaziz bffdf0ec6c On branch main
new file:   documentation/LLM_Information.md
	modified:   main_dc/certbot/scripts/checkRenewCerts.sh
renew checkRenewCerts replace line with expirY_unix parse date formate
2026-05-02 09:15:43 +05:00
valitovgaziz d9a4a579b7 modified: main_dc/valitovgaziz/html/index.html
fix bag
2026-04-16 16:24:14 +05:00
valitovgaziz 4e7a4f01ba modified: main_dc/valitovgaziz/html/index.html
add biography into my site valitovgaziz
fix resume location
2026-04-16 15:59:16 +05:00
valitovgaziz 979c265e36 On branch main
modified:   main_dc/yalarba/api_yal/internal/domain/account/handler.go
	modified:   main_dc/yalarba/api_yal/internal/domain/account/router.go
	modified:   main_dc/yalarba/api_yal/internal/domain/account/service.go
	modified:   main_dc/yalarba/api_yal/internal/domain/auth/router.go
	new file:   main_dc/yalarba/api_yal/internal/domain/comment/dto.go
	new file:   main_dc/yalarba/api_yal/internal/domain/feetback/dto.go
	new file:   main_dc/yalarba/api_yal/internal/domain/object/dto.go
	new file:   main_dc/yalarba/api_yal/internal/domain/object/errors.go
	new file:   main_dc/yalarba/api_yal/internal/domain/object/handler.go
	new file:   main_dc/yalarba/api_yal/internal/domain/object/router.go
	new file:   main_dc/yalarba/api_yal/internal/domain/object/service.go
	new file:   main_dc/yalarba/api_yal/internal/domain/object/types.go
	new file:   main_dc/yalarba/api_yal/internal/domain/rating/dto.go
	modified:   main_dc/yalarba/api_yal/internal/models/rating.go
add and not tested Object's domain
2026-03-31 16:53:24 +05:00
valitovgaziz 15eed69a45 On branch main
modified:   main_dc/yalarba/api_yal/.env
	modified:   main_dc/yalarba/api_yal/cmd/main.go
switch docker container iner port for tp_yal to 8787
2026-03-31 13:47:08 +05:00
valitovgaziz 75b2f3f6b2 On branch main
modified:   main_dc/yalarba/api_yal/internal/domain/account/dto.go
	new file:   main_dc/yalarba/api_yal/internal/domain/account/errors.go
	modified:   main_dc/yalarba/api_yal/internal/domain/account/handler.go
	modified:   main_dc/yalarba/api_yal/internal/domain/account/router.go
	modified:   main_dc/yalarba/api_yal/internal/domain/account/service.go
	new file:   main_dc/yalarba/api_yal/internal/domain/account/types.go
	new file:   main_dc/yalarba/api_yal/internal/middleware/admin.go
	modified:   main_dc/yalarba/api_yal/internal/middleware/auth.go
	new file:   main_dc/yalarba/api_yal/internal/middleware/context.go
	new file:   main_dc/yalarba/api_yal/internal/middleware/logging.go
	modified:   main_dc/yalarba/api_yal/internal/router/router.go
last but not yet commit
2026-03-31 09:43:18 +05:00
valitovgaziz f304f982c0 On branch main
modified:   main_dc/yalarba/api_yal/go.sum
	modified:   main_dc/yalarba/api_yal/internal/domain/account/dto.go
	modified:   main_dc/yalarba/api_yal/internal/models/account.go
	modified:   main_dc/yalarba/api_yal/internal/models/appeal.go
	modified:   main_dc/yalarba/api_yal/internal/models/comment.go
	modified:   main_dc/yalarba/api_yal/internal/models/feedback.go
	modified:   main_dc/yalarba/api_yal/internal/models/password_reset.go
	modified:   main_dc/yalarba/api_yal/internal/models/rating.go
	modified:   "main_dc/yalarba/api_yal/internal/models/\320\276bject.go"
set embedded base model anonimus as realy embedded struct
without set name for field base
2026-03-31 07:55:55 +05:00
valitovgaziz d9e04cf865 On branch main
modified:   main_dc/yalarba/api_yal/internal/domain/auth/handler.go
	modified:   main_dc/yalarba/api_yal/internal/domain/auth/servcie.go
implement generate refresh token
2026-03-31 07:05:13 +05:00
valitovgaziz 622638643c On branch main
modified:   main_dc/yalarba/api_yal/go.mod
	modified:   main_dc/yalarba/api_yal/go.sum
	modified:   main_dc/yalarba/api_yal/internal/domain/auth/dto.go
	modified:   main_dc/yalarba/api_yal/internal/domain/auth/handler.go
	modified:   main_dc/yalarba/api_yal/internal/domain/auth/router.go
	modified:   main_dc/yalarba/api_yal/internal/domain/auth/servcie.go
renew go.mod file
implememented auth domain
2026-03-31 05:46:08 +05:00
valitovgaziz 21c6c03b27 On branch main
modified:   internal/database/psql_db.go
	modified:   internal/domain/auth/dto.go
	modified:   internal/domain/auth/handler.go
	modified:   internal/domain/auth/router.go
	modified:   internal/domain/auth/servcie.go
	new file:   internal/models/password_reset.go
	modified:   internal/repository/account_repository.go
	modified:   internal/repository/account_repository_impl.go
auth domain is implemented but not tested
2026-03-31 05:29:46 +05:00
valitovgaziz 8b40d1bfe5 On branch main
modified:   internal/domain/auth/dto.go
	modified:   internal/domain/auth/handler.go
	modified:   internal/domain/auth/router.go
	modified:   internal/domain/auth/servcie.go
	modified:   internal/middleware/auth.go
	modified:   internal/router/router.go
auth implemented without reset password
2026-03-31 04:22:54 +05:00
valitovgaziz 659cd3584c On branch main
modified:   internal/domain/auth/handler.go
	modified:   internal/domain/auth/servcie.go
Refresh token is upadateble from now
2026-03-31 03:24:07 +05:00
valitovgaziz 8c63b1fbb9 modified: main_dc/yalarba/api_yal/internal/domain/auth/handler.go
modified:   main_dc/yalarba/api_yal/internal/domain/auth/servcie.go
change logger from debug to infolavel
2026-03-10 09:58:02 +05:00
valitovgaziz d45c5841dc modified: main_dc/yalarba/api_yal/go.mod
modified:   main_dc/yalarba/api_yal/go.sum
	modified:   main_dc/yalarba/api_yal/internal/domain/auth/dto.go
	modified:   main_dc/yalarba/api_yal/internal/domain/auth/handler.go
	modified:   main_dc/yalarba/api_yal/internal/domain/auth/router.go
	modified:   main_dc/yalarba/api_yal/internal/domain/auth/servcie.go
	new file:   main_dc/yalarba/api_yal/internal/middleware/auth.go
	deleted:    main_dc/yalarba/api_yal/internal/middleware/authMiddleware.go
	modified:   main_dc/yalarba/api_yal/internal/router/router.go
set auth domain, not tested
2026-03-10 00:35:25 +05:00
valitovgaziz 5561a9ee8c modified: main_dc/docker-compose.yml
modified:   main_dc/yalarba/api_yal/cmd/main.go
	modified:   main_dc/yalarba/api_yal/internal/domain/auth/dto.go
	modified:   main_dc/yalarba/api_yal/internal/domain/auth/handler.go
	modified:   main_dc/yalarba/api_yal/internal/domain/auth/servcie.go
add AuthRes and UserInfo structs for request after auth
2026-03-10 00:07:19 +05:00
valitovgaziz 0108b981ce modified: main_dc/docker-compose.yml
set config back
2026-03-09 22:05:28 +05:00
valitovgaziz 93f13e284d modified: main_dc/docker-compose.yml
set app config from .env for docker-compose.yml file
2026-03-09 22:04:25 +05:00
valitovgaziz 21cac5b43b modified: main_dc/yalarba/api_yal/cmd/main.go
set dependensy cfg to appPort from .env cofigis
2026-03-09 22:01:45 +05:00
valitovgaziz 0e499498f5 new file: internal/domain/account/dto.go
new file:   internal/domain/account/handler.go
	new file:   internal/domain/account/router.go
	new file:   internal/domain/account/service.go
	deleted:    internal/domain/auth/servcie_impl.go
add empty files for routing account struct
2026-03-09 14:02:16 +05:00
valitovgaziz 5ee46d02a9 modified: main_dc/yalarba/api_yal/internal/router/router.go
add auth routers into main router for register auth paths
2026-03-09 03:07:58 +05:00
valitovgaziz 7987472635 modified: main_dc/yalarba/api_yal/internal/domain/auth/handler.go
modified:   main_dc/yalarba/api_yal/internal/domain/auth/router.go
	modified:   main_dc/yalarba/api_yal/internal/domain/auth/servcie.go
set mock service for auth layer
2026-03-09 03:03:58 +05:00
valitovgaziz 313350c5f2 modified: main_dc/yalarba/api_yal/internal/router/router.go
fix bag with auth before router paths
2026-03-09 02:42:22 +05:00
valitovgaziz e9a21c758d modified: main_dc/yalarba/api_yal/internal/router/router.go
add comments and loggs for middleware func
2026-03-09 02:36:21 +05:00
valitovgaziz 74b46a5109 modified: main_dc/yalarba/api_yal/go.mod
modified:   main_dc/yalarba/api_yal/go.sum
	modified:   main_dc/yalarba/api_yal/internal/config/config.go
	modified:   main_dc/yalarba/api_yal/internal/router/router.go
add stock middleware from chi
2026-03-09 02:26:56 +05:00
valitovgaziz 1e263cf1a2 new file: main_dc/yalarba/api_yal/internal/middleware/authMiddleware.go
modified:   main_dc/yalarba/api_yal/internal/router/router.go
add middleware for auth but empty for time
2026-03-09 02:02:48 +05:00
valitovgaziz 55f2834617 new file: main_dc/yalarba/api_yal/internal/domain/auth/router.go
deleted:    main_dc/yalarba/api_yal/internal/handlers/auth.go
moove routing into layer
2026-03-05 13:04:06 +05:00
valitovgaziz 28103d3659 deleted: main_dc/yalarba/api_yal/internal/handlers/account.go
deleted:    main_dc/yalarba/api_yal/internal/handlers/allHandlers.go
	modified:   main_dc/yalarba/api_yal/internal/router/router.go
delete ald arch files
2026-03-04 17:37:05 +05:00
valitovgaziz 5dc8b5cf66 modified: main_dc/yalarba/api_yal/go.sum
renamed:    main_dc/yalarba/api_yal/internal/dto/account_dto.go -> main_dc/yalarba/api_yal/internal/domain/auth/dto.go
	new file:   main_dc/yalarba/api_yal/internal/domain/auth/handler.go
	new file:   main_dc/yalarba/api_yal/internal/domain/auth/servcie.go
	new file:   main_dc/yalarba/api_yal/internal/domain/auth/servcie_impl.go
	modified:   main_dc/yalarba/api_yal/internal/handlers/allHandlers.go
	modified:   main_dc/yalarba/api_yal/internal/handlers/auth.go
reimplement arch to ddd + layered structure
2026-03-04 17:32:39 +05:00
valitovgaziz c0dbfd02b8 modified: main_dc/yalarba/api_yal/go.mod
modified:   main_dc/yalarba/api_yal/go.sum
	new file:   main_dc/yalarba/api_yal/internal/dto/account_dto.go
	new file:   main_dc/yalarba/api_yal/internal/handlers/account.go
	modified:   main_dc/yalarba/api_yal/internal/handlers/auth.go
	modified:   main_dc/yalarba/api_yal/internal/router/router.go
	new file:   main_dc/yalarba/api_yal/internal/service/account_service.go
add some auth functions into api_yal
2026-03-04 14:12:35 +05:00
valitovgaziz 8b28a1ee01 new file: main_dc/yalarba/api_yal/internal/handlers/allHandlers.go
modified:   main_dc/yalarba/api_yal/internal/router/router.go
add allhandlers.go file
2026-03-04 06:54:02 +05:00
valitovgaziz 6c6811ef01 modified: main_dc/yalarba/api_yal/internal/handlers/auth.go
modified:   main_dc/yalarba/easySite/easySite/app/pages/auth/login.vue
add some handler and login into easys site login path
2026-03-03 21:59:23 +05:00
valitovgaziz f25dfeacb8 modified: go.mod
modified:   go.sum
	new file:   internal/logger/interface.go
	new file:   internal/logger/route_logger.go
	modified:   internal/router/router.go
add router logger that logg all router in the start api_yal
2026-03-03 15:26:29 +05:00
valitovgaziz 9a3ff9ad3d modified: main_dc/docker-compose.yml
modified:   main_dc/yalarba/api_yal/internal/router/router.go
change check path to health, delete router auth (for check)
2026-03-03 14:05:08 +05:00
valitovgaziz 5cd9254a97 modified: main_dc/docker-compose.yml
modified:   main_dc/yalarba/api_yal/internal/router/router.go
add depends on db to api_yal service
add auth path for check healt
2026-03-03 14:04:15 +05:00
valitovgaziz a07637e9da modified: cmd/main.go
modified:   internal/config/config.go
add some logs end env variables
2026-03-03 13:51:42 +05:00
valitovgaziz f33149dea2 modified: main_dc/yalarba/api_yal/internal/router/router.go
change route health_yal to health
2026-02-28 05:41:58 +05:00
valitovgaziz 39470b9707 modified: main_dc/yalarba/api_yal/cmd/main.go
write comment for godoc into main.go api_yal
2026-02-25 12:46:19 +05:00
valitovgaziz 97163dd0cc modified: main_dc/yalarba/api_yal/internal/router/router.go
add imports that not imported
2026-02-25 12:36:31 +05:00
valitovgaziz 06d3dbd8b7 modified: main_dc/yalarba/api_yal/cmd/main.go
modified:   main_dc/yalarba/api_yal/internal/router/router.go
set router
2026-02-25 12:34:32 +05:00
valitovgaziz 6c0eb6d877 modified: main_dc/yalarba/api_yal/go.mod
modified:   main_dc/yalarba/api_yal/go.sum
	new file:   main_dc/yalarba/api_yal/internal/router/router.go
add router.go into api_yal
2026-02-25 12:24:45 +05:00
valitovgaziz 104a006166 new file: main_dc/yalarba/api_yal/internal/repository/account_repository_impl.go
new file:   main_dc/yalarba/api_yal/internal/repository/appeal_repository_impl.go
	new file:   main_dc/yalarba/api_yal/internal/repository/comment_repository_impl.go
	new file:   main_dc/yalarba/api_yal/internal/repository/feedback_repository_impl.go
	new file:   main_dc/yalarba/api_yal/internal/repository/object_repository_impl.go
	new file:   main_dc/yalarba/api_yal/internal/repository/rating_repository_impl.go
implement interfaces for repositories
2026-02-24 13:10:14 +05:00
valitovgaziz 8e3a20ee48 new file: main_dc/yalarba/api_yal/internal/repository/account_repository.go
new file:   main_dc/yalarba/api_yal/internal/repository/appeal_repository.go
	new file:   main_dc/yalarba/api_yal/internal/repository/comment_repository.go
	new file:   main_dc/yalarba/api_yal/internal/repository/feedback_repository.go
	new file:   main_dc/yalarba/api_yal/internal/repository/object_repository.go
	new file:   main_dc/yalarba/api_yal/internal/repository/rating_repository.go
add repositoryes for models into yal db
2026-02-24 12:54:46 +05:00
valitovgaziz 4dbf58d2dc deleted: main_dc/yalarba/api_yal/internal/middleware/auth.go
deleted:    main_dc/yalarba/api_yal/internal/models/authentication.go
I need to write it for myself
2026-02-24 00:20:24 +05:00
valitovgaziz 9f5cef41fc new file: main_dc/yalarba/api_yal/internal/middleware/auth.go
new file:   main_dc/yalarba/api_yal/internal/models/authentication.go
add auth object for create authenticate endpoints
2026-02-23 21:41:55 +05:00
valitovgaziz 093e95ce17 modified: main_dc/yalarba/api_yal/internal/database/psql_db.go
add appealHistory into autoMigrate
2026-02-23 02:22:08 +05:00
valitovgaziz 45062ca610 modified: main_dc/yalarba/api_yal/internal/database/psql_db.go
new file:   main_dc/yalarba/api_yal/internal/models/appeal.go
add appeal struct and add it into autoMigrate
2026-02-23 02:15:52 +05:00
valitovgaziz 422574b685 modified: main_dc/BB/bbvue/yandex_ee0d6f6af1479298.html
to shore the own for yandex
2026-02-23 01:50:21 +05:00
valitovgaziz c0734f50e8 modified: main_dc/yalarba/api_yal/internal/database/psql_db.go
new file:   main_dc/yalarba/api_yal/internal/models/comment.go
	modified:   main_dc/yalarba/api_yal/internal/models/feedback.go
add comment into models
add comment into feedback
add comment into autoMigrate psql_db.go
2026-02-22 14:04:55 +05:00
valitovgaziz 7ae2acfd8c modified: main_dc/yalarba/api_yal/internal/models/feedback.go
upgrade feedback.go, add Platform type and score atributs
2026-02-22 13:53:23 +05:00
valitovgaziz 45602a4628 modified: main_dc/valitovgaziz/html/index.html
renamed:    main_dc/valitovgaziz/resume/resume.html -> main_dc/valitovgaziz/html/resume/resume.html
	renamed:    main_dc/valitovgaziz/resume/style/main.css -> main_dc/valitovgaziz/html/resume/style/main.css
moove resume html file into html directory
2026-02-22 10:33:01 +05:00
valitovgaziz f2a4fe1c63 modified: main_dc/valitovgaziz/resume/resume.html
fix bag with main.css path from resume page
2026-02-22 10:30:26 +05:00
valitovgaziz 085b2efde8 modified: main_dc/valitovgaziz/html/style/hero_section.css
fix bag with not closed curly brackets
2026-02-22 08:12:05 +05:00
valitovgaziz d2f4a5bf8b modified: main_dc/valitovgaziz/html/style/hero_section.css
backgroud for header set to non transparen color
2026-02-22 08:09:25 +05:00
valitovgaziz 3477fdf855 modified: main_dc/valitovgaziz/html/index.html
modified:   main_dc/valitovgaziz/html/style.css
	modified:   main_dc/valitovgaziz/html/style/hero_section.css
valitovgaziz site add resume page and set some more pretty sytles
2026-02-22 08:06:35 +05:00
valitovgaziz 9b25735fae renamed: resume/resume.html -> main_dc/valitovgaziz/resume/resume.html
renamed:    resume/style/main.css -> main_dc/valitovgaziz/resume/style/main.css
moove resume to valitovgaziz site
2026-02-22 07:34:49 +05:00
valitovgaziz dced067fc3 deleted: 2
new file:   resume/resume.html
	new file:   resume/style/main.css
add resume site
2026-02-22 07:31:47 +05:00
valitovgaziz b7138865f5 new file: main_dc/BB/bbvue/yandex_ee0d6f6af1479298.html
add file for verification BB.vue
2026-02-22 06:10:21 +05:00
valitovgaziz 52c2cb5aa0 modified: main_dc/valitovgaziz/html/digital_background.js
minify digitals for beter perfomance
2026-02-20 08:46:54 +05:00
valitovgaziz cd53d2a1e5 modified: main_dc/yalarba/easySite/easySite/app/components/layout/Careers.vue
fix mistakes
2026-02-16 04:53:51 +05:00
valitovgaziz 1a14dd965e modified: "main_dc/yalarba/api_yal/internal/models/\320\276bject.go"
upgare object.go 1
2026-02-15 12:06:55 +05:00
valitovgaziz b11abaa16f modified: main_dc/yalarba/api_yal/internal/database/psql_db.go
add to automigrate the Feedback struct
2026-02-15 08:04:00 +05:00
valitovgaziz 80db6c6ee4 new file: main_dc/yalarba/api_yal/internal/models/feedback.go
create Feedback struct
2026-02-15 08:02:09 +05:00
valitovgaziz 9f9986dbdb new file: 2
modified:   main_dc/yalarba/api_yal/internal/models/rating.go
fix sicle links for VoteBreakdown struct and rating
2026-02-12 04:29:41 +05:00
valitovgaziz f40d3c92d0 modified: main_dc/yalarba/api_yal/internal/models/rating.go
fix bag with cicle links into rating.go struct
2026-02-12 04:27:48 +05:00
valitovgaziz e1735772e1 modified: "main_dc/yalarba/api_yal/internal/models/\320\276bject.go"
fix bag with sicle recursive type links
2026-02-12 04:26:13 +05:00
valitovgaziz ec2184dfea modified: main_dc/yalarba/api_yal/internal/models/rating.go
set comment into cod
2026-02-12 04:21:30 +05:00
valitovgaziz 6cc9354cce modified: main_dc/yalarba/api_yal/internal/database/psql_db.go
modified:   main_dc/yalarba/api_yal/internal/models/account.go
	new file:   main_dc/yalarba/api_yal/internal/models/rating.go
	modified:   "main_dc/yalarba/api_yal/internal/models/\320\276bject.go"
add rating objects, mogration for them
2026-02-12 04:17:42 +05:00
valitovgaziz f20d42e026 modified: main_dc/Makefile
upgade command for api_bb
2026-02-12 03:44:52 +05:00
valitovgaziz b689c55bdc modified: main_dc/yalarba/api_yal/Dockerfile
upgrade the api_yal to 1.26.0-alpine version golang
2026-02-12 03:39:24 +05:00
valitovgaziz f139cf622e modified: main_dc/BB/api_bb/Dockerfile
api_bb upgrade to 1.26.0-alpine version
2026-02-12 03:37:11 +05:00
valitovgaziz 66d52102ea modified: main_dc/yalarba/easySite/easySite/app/pages/contact/index.vue
fix bag with doubles header and footer easysite102.ru contacs page
2026-02-11 19:57:27 +05:00
valitovgaziz 94379af2c9 modified: main_dc/yalarba/easySite/easySite/app/pages/support/index.vue
easysite102.ru fix bag with dowble footer & header in page support
2026-02-11 19:52:13 +05:00
valitovgaziz 4e3cdb8ad9 modified: main_dc/yalarba/api_yal/internal/models/account.go
modified:   "main_dc/yalarba/api_yal/internal/models/\320\276bject.go"
for eagerloading use references
2026-02-11 07:08:54 +05:00
valitovgaziz da90a21fd3 modified: "main_dc/yalarba/api_yal/internal/models/\320\276bject.go"
add ownerID into Object
2026-02-11 07:01:37 +05:00
valitovgaziz bc4ecc0564 modified: main_dc/yalarba/api_yal/internal/models/account.go
fix bag with Object it was cirillic literal into Object struct name
2026-02-11 06:56:35 +05:00
valitovgaziz 4014249d64 modified: main_dc/yalarba/api_yal/internal/models/account.go
modified:   "main_dc/yalarba/api_yal/internal/models/\320\276bject.go"
fix bag with updatfind object into account
2026-02-11 06:51:36 +05:00
valitovgaziz 9ae4eeb33f modified: main_dc/yalarba/api_yal/internal/models/base.go
modified:   "main_dc/yalarba/api_yal/internal/models/\320\276bject.go"
delete UpdateHistory from base struct
2026-02-11 06:46:16 +05:00
valitovgaziz 49b9805cbe modified: main_dc/yalarba/api_yal/internal/database/psql_db.go
modified:   main_dc/yalarba/api_yal/internal/models/account.go
	renamed:    main_dc/yalarba/api_yal/internal/models/Object.go -> "main_dc/yalarba/api_yal/internal/models/\320\276bject.go"
add Onwer Account ass foregn key into Object
2026-02-11 06:30:14 +05:00
valitovgaziz f3cf18b1bb new file: main_dc/yalarba/api_yal/internal/models/Object.go
add Object
2026-02-11 05:18:31 +05:00
valitovgaziz 92f18e15b9 modified: main_dc/docker-compose.yml
deleted:    main_dc/yalarba/api_yal/.env copy
	modified:   main_dc/yalarba/api_yal/cmd/main.go
	modified:   main_dc/yalarba/api_yal/go.mod
	modified:   main_dc/yalarba/api_yal/go.sum
fix config, add config file path into docker-compose.yaml file
2026-02-09 05:13:30 +05:00
valitovgaziz 5b7b7f122a modified: main_dc/yalarba/api_yal/.env
change db name to db_yal
2026-02-09 05:00:43 +05:00
valitovgaziz 5da0d35efe modified: main_dc/yalarba/api_yal/.env
fix to host - db_tp
2026-02-09 04:53:50 +05:00
valitovgaziz 33c4ff959e modified: main_dc/yalarba/api_yal/.env
change db_host to  db
2026-02-09 04:49:29 +05:00
valitovgaziz 50c3375abd modified: main_dc/yalarba/api_yal/.env
modifay config
2026-02-09 04:39:44 +05:00
valitovgaziz 1314b24370 modified: main_dc/yalarba/api_yal/.env
fix configs for api_yal
2026-02-07 06:53:08 +05:00
valitovgaziz 3bc5c16711 modified: main_dc/Makefile
add commands for api_yal restart start rebuild
2026-02-07 05:52:59 +05:00
valitovgaziz 6928b93812 modified: main_dc/yalarba/api_yal/internal/database/psql_db.go
set automigrate
2026-02-07 05:41:25 +05:00
valitovgaziz ad55824fd6 modified: main_dc/yalarba/api_yal/cmd/main.go
modified:   main_dc/yalarba/api_yal/go.mod
	modified:   main_dc/yalarba/api_yal/go.sum
	new file:   main_dc/yalarba/api_yal/internal/database/psql_db.go
add connection to db without tests api_yal
2026-02-07 05:39:57 +05:00
valitovgaziz 37409a033e modified: main_dc/yalarba/api_yal/cmd/main.go
fix mistakes
2026-02-07 05:31:58 +05:00
valitovgaziz 5a3874ac90 modified: main_dc/yalarba/api_yal/cmd/main.go
add logs into main api_yal
2026-02-07 05:30:38 +05:00
valitovgaziz fa174f7ca2 modified: cmd/main.go
modified:   go.mod
	modified:   go.sum
	new file:   internal/logger/logger.go
add zapplogger initiator
2026-02-02 04:12:07 +05:00
valitovgaziz 6f2d3156ca new file: .env copy
new file:   internal/config/config.go
add configs .env file and config loader
2026-02-02 04:06:19 +05:00
valitovgaziz 988416199b rerge branch 'main' of github.com:valitovgaziz/tp
merge
2026-02-02 03:58:56 +05:00
valitovgaziz c106fc3921 modified: main_dc/yalarba/api_yal/go.mod
modified:   main_dc/yalarba/api_yal/go.sum
	renamed:    main_dc/yalarba/api_es/internal/models/new/account.go -> main_dc/yalarba/api_yal/internal/models/account.go
	renamed:    main_dc/yalarba/api_es/internal/models/new/base.go -> main_dc/yalarba/api_yal/internal/models/base.go
	renamed:    main_dc/yalarba/api_es/internal/models/new/update_history.go -> main_dc/yalarba/api_yal/internal/models/update_history.go
moove new models for full api
2026-02-02 03:58:09 +05:00
valitovgaziz 815a47b9bb modified: main_dc/valitovgaziz/html/blog.html
add one aticle into blog valitovgaziz.ru site
2026-01-30 17:01:28 +05:00
valitovgaziz 46d11edfe0 modified: main_dc/yalarba/api_yal/Dockerfile
fix dockerfile
2026-01-30 05:59:00 +05:00
valitovgaziz 820fd91f90 new file: main_dc/yalarba/api_yal/go.sum
add go.sum file
2026-01-30 05:06:59 +05:00
valitovgaziz dea499f9bc modified: main_dc/Makefile
modified:   main_dc/docker-compose.yml
fix path
2026-01-30 05:05:38 +05:00
valitovgaziz 5a6314613a modified: main_dc/docker-compose.yml
modified:   main_dc/yalarba/api_yal/Dockerfile
add service into docker-compose.yml, upgrade the Dcoekrfile for api_yal
2026-01-30 04:49:52 +05:00
valitovgaziz a72a13fd5f modified: main_dc/nginx/nginx-ssl.conf
add location auth with proxy to api_yal into ingin configs
2026-01-30 04:43:49 +05:00
valitovgaziz bc18d02935 modified: Dockerfile
new file:   bin/api_yal
	new file:   cmd/main.go
	new file:   go.mod
	new file:   internal/handlers/auth.go
	new file:   internal/server/server.go
add server, files, dockerfile, build
2026-01-30 04:41:27 +05:00
valitovgaziz ed2f24bc94 new file: main_dc/yalarba/api_yal/.env
new file:   main_dc/yalarba/api_yal/Dockerfile
	new file:   main_dc/yalarba/api_yal/readme.md
start new api for yalarba and easysite102.ru
2026-01-30 03:56:39 +05:00
valitovgaziz eef5cdc1fc renamed: main_dc/yalarba/api_es/internal/models/account.go -> main_dc/yalarba/api_es/internal/models/new/account.go
renamed:    main_dc/yalarba/api_es/internal/models/base.go -> main_dc/yalarba/api_es/internal/models/new/base.go
	renamed:    main_dc/yalarba/api_es/internal/models/update_history.go -> main_dc/yalarba/api_es/internal/models/new/update_history.go
rename
2026-01-30 03:53:10 +05:00
valitovgaziz fc9372db2e modified: main_dc/Makefile
delete redandant command for restart all services
2026-01-27 20:12:29 +05:00
valitovgaziz a2ad1ead0d modified: main_dc/valitovgaziz/html/blog.html
last writes on blog page valitovgaziz.ru site
2026-01-25 02:07:57 +05:00
valitovgaziz caa971ad98 modified: main_dc/valitovgaziz/html/blog.html
fix some mistake about date
2026-01-25 02:03:15 +05:00
valitovgaziz 4206ef2b7f modified: main_dc/valitovgaziz/html/index.html
add link into main page valitovgaziz.ru to my blog page
2026-01-25 01:50:41 +05:00
valitovgaziz 9d7d83cdc2 modified: main_dc/valitovgaziz/html/blog.html
modified:   main_dc/valitovgaziz/html/style/blog.css
add css and renew design CSS
2026-01-25 01:43:36 +05:00
valitovgaziz e77fa69e89 new file: main_dc/valitovgaziz/html/blog.html
new file:   main_dc/valitovgaziz/html/style/blog.css
add blog page
2026-01-25 01:34:46 +05:00
valitovgaziz ec00612e76 modified: main_dc/Makefile
make cerbot last command change to wat from wn
2026-01-23 00:44:44 +05:00
valitovgaziz db8497661a modified: main_dc/certbot/scripts/checkRenewCerts.sh
change warnign day before expire certs from 2 to 5 days
2026-01-23 00:40:25 +05:00
valitovgaziz c8b2f69482 modified: main_dc/yalarba/api_es/internal/models/account.go
add status code to Account struct easysite102.ru
2026-01-22 22:03:10 +05:00
valitovgaziz b33127055a modified: main_dc/yalarba/api_es/internal/models/account.go
add business info into Account easysite102.ru
2026-01-22 16:55:39 +05:00
valitovgaziz a228adebc7 modified: main_dc/yalarba/api_es/internal/models/account.go
add account main info
2026-01-22 16:22:44 +05:00
valitovgaziz ea87f024c3 modified: main_dc/yalarba/api_es/internal/models/account.go
modified:   main_dc/yalarba/api_es/internal/models/base.go
prettify it
2026-01-22 15:48:34 +05:00
valitovgaziz 4c5a7af5f5 new file: main_dc/yalarba/api_es/internal/models/account.go
modified:   main_dc/yalarba/api_es/internal/models/base.go
	modified:   main_dc/yalarba/api_es/internal/models/update_history.go
fix update history, add json
set Base without polymorphicValue
create Account object but not finish
2026-01-22 15:43:03 +05:00
valitovgaziz 276e94c460 modified: main_dc/yalarba/api_es/internal/models/base.go
new file:   main_dc/yalarba/api_es/internal/models/update_history.go
add UpdateHistory model into easysite102.ru
2026-01-22 04:52:38 +05:00
valitovgaziz 9ee12e4c34 new file: main_dc/yalarba/api_es/internal/models/base.go
add base model into easysite102.ru
2026-01-22 04:03:00 +05:00
valitovgaziz e1d801d96e modified: main_dc/docker-compose.yml
modified:   main_dc/yalarba/README.md
fix mistakes
2026-01-22 03:50:01 +05:00
valitovgaziz 484e1f9155 modified: main_dc/yalarba/api_es/cmd/main.go
add detailed comments
2026-01-12 03:36:18 +05:00
valitovgaziz 1db2469891 new file: main_dc/yalarba/tourism_types/risk_factors.txt
new file:   main_dc/yalarba/tourism_types/tourism_subtypes/aquatic.txt
	new file:   main_dc/yalarba/tourism_types/tourism_subtypes/business_tourism_subtypes.txt
	new file:   main_dc/yalarba/tourism_types/tourism_subtypes/children_youth_tourism_subtypes.txt
	new file:   main_dc/yalarba/tourism_types/tourism_subtypes/cultural_heritage_objects.txt
	new file:   main_dc/yalarba/tourism_types/tourism_subtypes/extreme_adventures.txt
new file:   main_dc/yalarba/tourism_types/tourism_subtypes/group_types.txt
	new file:   main_dc/yalarba/tourism_types/tourism_subtypes/naturally_acitve.txt
	new file:   main_dc/yalarba/tourism_types/tourism_subtypes/recreational_tourism_subtypes.txt
	new file:   main_dc/yalarba/tourism_types/tourism_subtypes/religious_tourism_subtypes.txt
	new file:   main_dc/yalarba/tourism_types/tourism_subtypes/special.txt
	new file:   main_dc/yalarba/tourism_types/tourism_types.txt
	new file:   main_dc/yalarba/tourism_types/tourist_age_groups.txt
	new file:   main_dc/yalarba/tourism_types/transport_types.txt
add tourism types and subtypes
2026-01-11 04:17:55 +05:00
valitovgaziz d8acbcf432 modified: main_dc/yalarba/easySite/easySite/app/pages/rules/index.vue
fix rules page doubled header and footer
2026-01-07 10:09:57 +05:00
valitovgaziz 70cd42e660 modified: main_dc/yalarba/easySite/easySite/app/pages/vacations/index.vue
fix header and footer  dowbling
2026-01-03 22:45:04 +05:00
valitovgaziz 79d0dd3af2 renamed: main_dc/BB/bbvue/public/images/FastRun.jpg -> main_dc/BB/bbvue/public/images/homePagePhoto/FastRun.jpg
new file:   main_dc/BB/bbvue/public/images/homePagePhoto/Magnitogorsk_2025_december.jpg
	renamed:    main_dc/BB/bbvue/public/images/slide2.jpg -> main_dc/BB/bbvue/public/images/homePagePhoto/slide2.jpg
	renamed:    main_dc/BB/bbvue/public/images/slide3.jpg -> main_dc/BB/bbvue/public/images/homePagePhoto/slide3.jpg
	renamed:    main_dc/BB/bbvue/public/images/slide4.jpg -> main_dc/BB/bbvue/public/images/homePagePhoto/slide4.jpg
	modified:   main_dc/BB/bbvue/src/views/Home.vue
change set photo for home page
2025-12-13 23:03:13 +05:00
valitovgaziz 28f4a65702 modified: main_dc/BB/bbvue/src/views/Gallery.vue
change gallary and add photos from magnitogorsk
2025-12-13 21:20:57 +05:00
valitovgaziz c2c05291cd new file: main_dc/BB/bbvue/public/images/ivents/magnitogorsk_2025/bb1.jpg
new file:   main_dc/BB/bbvue/public/images/ivents/magnitogorsk_2025/bb2.jpg
	new file:   main_dc/BB/bbvue/public/images/ivents/magnitogorsk_2025/bb3.jpg
	new file:   main_dc/BB/bbvue/public/images/ivents/magnitogorsk_2025/bb4.jpg
	new file:   main_dc/BB/bbvue/public/images/ivents/magnitogorsk_2025/bb5.jpg
	new file:   main_dc/BB/bbvue/public/images/ivents/magnitogorsk_2025/bb6.jpg
	new file:   main_dc/BB/bbvue/public/images/ivents/magnitogorsk_2025/bb7.jpg
	new file:   main_dc/BB/bbvue/public/images/ivents/magnitogorsk_2025/bb8.jpg
add photo from magnit to begushiybashkir.ru
2025-12-13 20:44:43 +05:00
valitovgaziz bce0a3e7c8 modified: main_dc/nginx/nginx-ssl.conf
add comments for nginx configs
2025-12-11 14:33:05 +05:00
valitovgaziz 2489058c48 modified: main_dc/nginx/switch-config.sh
add coments
2025-12-10 14:39:35 +05:00
valitovgaziz 97d663704b modified: main_dc/stubSite/style.css
fix problem with css
2025-12-08 17:07:31 +05:00
valitovgaziz 739802af81 modified: main_dc/stubSite/index.html
new file:   main_dc/stubSite/style.css
renew stub site
2025-12-08 17:04:43 +05:00
valitovgaziz 61db80d6cb renamed: documentation/DocerComposeYamlDocs.md -> documentation/DocerDocs.md
rename docs
2025-12-08 16:54:08 +05:00
valitovgaziz b8d3862ad9 new file: main_dc/stubSite/README.md
add README.md into stubsite
2025-12-04 07:59:51 +05:00
valitovgaziz 13593ccd77 deleted: main_dc/keycloak/.env.keycloak
deleted:    main_dc/keycloak/Dockerfile
	deleted:    main_dc/keycloak/keycloak.conf
	deleted:    main_dc/keycloak/realm-config/setup-realm.json
2025-12-04 07:54:54 +05:00
valitovgaziz daac28b869 new file: main_dc/BB/README.md
new file:   main_dc/BB/documentation/docs.md
add general and README.md file for BB project
2025-12-04 07:28:54 +05:00
valitovgaziz 1776b8c410 new file: main_dc/yalarba/README.md
new file:   main_dc/yalarba/documentation/docs.md
add documentation for all system yaltrip
2025-12-04 07:09:57 +05:00
valitovgaziz a824a55319 deleted: main_dc/yalarba/serv_spa/.env 2025-12-04 06:59:57 +05:00
valitovgaziz 4aa41a5fe3 modified: main_dc/yalarba/serv_spa/spa/vue/README.md
new file:   main_dc/yalarba/serv_spa/spa/vue/documentation/docs.md
	renamed:    main_dc/yalarba/serv_spa/spa/vue/advices.txt -> main_dc/yalarba/serv_spa/spa/vue/src/assets/advices.txt
add documentation for yalarba.ru
2025-12-03 21:19:24 +05:00
valitovgaziz 299cdcb73c new file: main_dc/valitovgaziz/documentaion/docs.md
add documentation for valitovgaziz.ru site
2025-12-03 07:00:15 +05:00
valitovgaziz 49136e6529 new file: main_dc/certbot/documentation/docs.md
add cerbot documentation
2025-12-03 06:55:31 +05:00
valitovgaziz ff81cf165b new file: main_dc/nginx/documentation/docs.md
add nignx documetation
2025-12-03 06:51:05 +05:00
valitovgaziz 0a8e51a349 new file: main_dc/BB/api_bb/documentation/docs.md
modified:   main_dc/BB/api_bb/readme.md
add docs for REST API begushiybashkir.ru
2025-12-02 21:00:17 +05:00
valitovgaziz 06bb95dc29 new file: main_dc/BB/bbvue/documentation/docs.md
Add docs for FronEnd begushiybashkir.ru
2025-12-02 20:53:24 +05:00
valitovgaziz 519da05072 new file: main_dc/yalarba/easySite/easySite/documentation/docs.md
add documentation for easysite102.ru nuxt.js fronend
2025-12-02 20:37:51 +05:00
valitovgaziz 7c1974ba9a new file: main_dc/yalarba/api_tp/documentation/docs.md
add docmentaion for yalarba.ru REST API service
2025-12-02 19:50:43 +05:00
valitovgaziz c7e7fb42a4 new file: main_dc/yalarba/api_es/documentation/docs.md
add docs for easysite REST API service api_es
2025-12-02 19:34:58 +05:00
valitovgaziz f98f6e3696 modified: README.md 2025-12-02 07:55:28 +05:00
valitovgaziz 803c0c6f50 modified: README.md
one more for su
2025-12-01 23:17:14 +05:00
valitovgaziz 37b9869188 modified: README.md
ended second task from README.md
2025-12-01 23:12:02 +05:00
valitovgaziz 7e0401042b modified: README.md
tasks
2025-12-01 23:10:36 +05:00
valitovgaziz dcad7d293a modified: documentation/RestartDocs.md
add server OS name
2025-11-30 21:40:16 +05:00
valitovgaziz 3169dfc960 renamed: documentation/DockerComposeDocumentation.md -> documentation/DocerComposeYamlDocs.md
new file:   documentation/RestartDocs.md
add docker for restart system on server
2025-11-30 21:39:26 +05:00
valitovgaziz c14556565b deleted: htmlCssJS_Poligon/index.html
delte redandant file
2025-11-30 17:18:04 +05:00
valitovgaziz 9c766cf0d4 new file: main_dc/valitovgaziz/README.md
add README.md file for valitovgaziz.ru site
2025-11-30 17:15:29 +05:00
valitovgaziz da974d946c deleted: auto_restart_services/docker-compose-up.service
delete auto restart servcie, becouse it stop and delete into
server:depricated
2025-11-29 16:08:49 +05:00
valitovgaziz 75242b415a deleted: main_dc/yalarba/serv_spa/spa/app/assets/bage_logo.png
deleted:    main_dc/yalarba/serv_spa/spa/app/index.html
	deleted:    main_dc/yalarba/serv_spa/spa/app/styles/mainStyle.css
delete ald site of yalarbar.u
2025-11-29 15:43:18 +05:00
valitovgaziz e85426d122 modified: main_dc/yalarba/serv_spa/spa/vue/package-lock.json
modified:   main_dc/yalarba/serv_spa/spa/vue/package.json
install vitte into serv_spa yalarba.ru site
2025-11-29 15:42:12 +05:00
valitovgaziz 9bb6ce0955 new file: documentation/DockerComposeDocumentation.md
add documentaion file for docker-compose.yaml file
2025-11-29 14:08:04 +05:00
valitovgaziz a2b8e23849 modified: main_dc/.env
modified:   main_dc/docker-compose.yml
upgrade docker-compose.yaml
2025-11-29 12:37:29 +05:00
valitovgaziz dc1c22e1c5 modified: main_dc/Makefile
Добавил комментарии для Makefile
2025-11-29 12:23:44 +05:00
valitovgaziz e80cd96668 renamed: MakefileDocs.md -> documentation/MakefileDocs.md
add directionry for documentation
2025-11-28 17:39:39 +05:00
valitovgaziz ed8c74c30e renamed: MakefileREADME.md -> MakefileDocs.md
rename
2025-11-28 16:33:26 +05:00
valitovgaziz 3792d3dc11 modified: main_dc/docker-compose.yml
add comments for services
2025-11-28 16:30:36 +05:00
valitovgaziz 34d7ae9de7 modified: main_dc/BB/bbvue/package-lock.json
modified:   main_dc/yalarba/easySite/easySite/package-lock.json
	modified:   main_dc/yalarba/serv_spa/spa/vue/package-lock.json
	modified:   package-lock.json
	modified:   package.json
eslint vue plugin added
2025-11-27 15:41:49 +05:00
valitovgaziz 9a8ac26da1 new file: MakefileREADME.md
add docs for Makefile
2025-11-27 10:37:42 +05:00
valitovgaziz 13da0e85cd modified: README.md
add little docs for restart server
2025-11-27 10:33:28 +05:00
valitovgaziz 61a12979a0 modified: main_dc/docker-compose.yml
add reboot uplees stoped for db's tp and bb
2025-11-27 06:52:11 +05:00
valitovgaziz 6669278c47 modified: main_dc/docker-compose.yml
add comments for docker-compose.yaml file volumes
2025-11-26 22:06:22 +05:00
valitovgaziz ffb062aa43 modified: main_dc/docker-compose.yml
add comments for docker compose yaml file volume
2025-11-26 19:09:10 +05:00
valitovgaziz 0c4bad7643 modified: main_dc/Makefile
change command
2025-11-26 15:04:00 +05:00
valitovgaziz 15e6c5eba3 modified: main_dc/BB/bbvue/src/views/Training.vue
change training plan on begushiybashkir.ru site
2025-11-26 15:02:35 +05:00
valitovgaziz 716998a3d6 modified: README.md
add docs
2025-11-26 13:54:05 +05:00
valitovgaziz db034ec2f2 modified: README.md
add little bit docs
2025-11-26 13:04:02 +05:00
valitovgaziz b88a2c5e46 modified: README.md
add task
2025-11-26 12:56:08 +05:00
valitovgaziz 9716c88400 modified: main_dc/valitovgaziz/html/style.css
renamed:    main_dc/valitovgaziz/html/darkTheme.css -> main_dc/valitovgaziz/html/style/darkTheme.css
	renamed:    main_dc/valitovgaziz/html/saveContactsButtonStyle.css -> main_dc/valitovgaziz/html/style/saveContactsButtonStyle.css
change files estimate
2025-11-25 19:03:47 +05:00
valitovgaziz d0d6ca63ed modified: main_dc/BB/bbvue/src/App.vue
add comments
2025-11-23 15:51:44 +05:00
valitovgaziz eb601a1cf1 modified: .gitignore
modified:   main_dc/BB/bbvue/src/App.vue
add bashkir language footer added
2025-11-23 15:25:51 +05:00
valitovgaziz de017cbe6c deleted: main_dc/yalarba/api_es/internal/models/busines_object.go
new file:   main_dc/yalarba/api_es/internal/repository/review_repository.go
add review repository
2025-11-21 16:48:09 +05:00
valitovgaziz ca44a2e6a5 new file: main_dc/yalarba/api_es/internal/repository/object_repository.go
add object_repository
2025-11-21 16:27:13 +05:00
valitovgaziz d2f526629e modified: README.md
add tasks into README.md file
2025-11-21 16:18:36 +05:00
valitovgaziz 99baf71ffc modified: main_dc/nginx/nginx-ssl.conf
add port 8080 inner port conteiners for api_bb
2025-11-19 06:37:36 +05:00
valitovgaziz 293443cc89 modified: main_dc/nginx/nginx-ssl.conf
add to nginx ssl configs perflite option header and cors
2025-11-19 06:29:58 +05:00
valitovgaziz 31b06df84e modified: main_dc/Makefile
change vue_bb command with wn
2025-11-19 06:19:56 +05:00
valitovgaziz 2381ab1c27 modified: main_dc/BB/bbvue/src/App.vue
modified:   main_dc/BB/bbvue/src/views/Login.vue
css rule for center vertical and with login form
2025-11-19 06:18:32 +05:00
valitovgaziz 31ec0e0cb5 modified: main_dc/BB/bbvue/src/views/Members.vue
modified:   main_dc/BB/bbvue/src/views/Reviews.vue
delete dark scheema from review apge and Members page begushiybashkir
site
2025-11-19 05:52:18 +05:00
valitovgaziz b348adc65a modified: main_dc/Makefile
add all commands for all cicle
2025-11-19 05:42:14 +05:00
valitovgaziz 1ede01264f modified: main_dc/Makefile
restart and wn to all
2025-11-19 05:40:17 +05:00
valitovgaziz 826583a5ff modified: main_dc/Makefile
add restart_all command
2025-11-19 05:39:17 +05:00
valitovgaziz 2440e5d359 modified: main_dc/docker-compose.yml
set interval to 30s for healthcheck for all containers
2025-11-19 05:37:09 +05:00
valitovgaziz c0eed7faf8 modified: README.md
delete reviews and set them into reviews page begushiybashkir.ru site
2025-11-19 05:29:34 +05:00
valitovgaziz 1817a3ba2e modified: main_dc/yalarba/easySite/easySite/app/pages/about/index.vue
delete overhead header and footer components from about page
easysite102.ru site
2025-11-19 05:23:08 +05:00
valitovgaziz e2409b58bd modified: main_dc/Makefile
change vue_bb command
2025-11-19 05:18:56 +05:00
valitovgaziz b51aa3f2e8 modified: main_dc/Makefile
change command for bbvue build
2025-11-19 05:17:55 +05:00
valitovgaziz a8acfbbed3 modified: main_dc/BB/bbvue/src/views/Reviews.vue
add reviews on reviews page begushiybashkir.ru site
2025-11-19 05:14:49 +05:00
valitovgaziz a574a7eac7 modified: README.md
set task direction
2025-11-18 21:58:54 +05:00
valitovgaziz 8d80fd4bc6 modified: main_dc/Makefile
agane
2025-11-17 22:51:48 +05:00
valitovgaziz cb83f271ef modified: main_dc/Makefile
api => api_tp
2025-11-17 22:50:52 +05:00
valitovgaziz ff7b3e612e modified: main_dc/docker-compose.yml
set little time interval
2025-11-17 22:50:00 +05:00
valitovgaziz 83735ac22f modified: main_dc/docker-compose.yml
change depends on nameing api to api_tp
2025-11-17 22:28:37 +05:00
valitovgaziz d011ddaa37 modified: main_dc/docker-compose.yml
modified:   main_dc/nginx/nginx-ssl.conf
change name on nginx ssh config to api_tp
2025-11-17 22:26:18 +05:00
valitovgaziz 4bd82cb2d5 modified: main_dc/Makefile
add api commands for Makefile
2025-11-17 21:54:12 +05:00
valitovgaziz cbd8051f03 deleted: main_dc/yalarba/api_tp/internal/handlers/oauth_VK.go
deleted:    main_dc/yalarba/api_tp/internal/handlers/oauth_yandex.go
	deleted:    main_dc/yalarba/api_tp/internal/models/contacts.go
	deleted:    main_dc/yalarba/api_tp/internal/models/o_auth_provider.go
	modified:   main_dc/yalarba/api_tp/internal/server/server.go
	deleted:    main_dc/yalarba/api_tp/internal/utils/oauth_utils.go
	modified:   main_dc/yalarba/api_tp/pkg/database/postgres.go
remove uAuth from api
2025-11-17 21:47:08 +05:00
valitovgaziz a3729e39ba modified: main_dc/yalarba/api_tp/internal/models/user.go
remove rel from UserT
2025-11-17 21:43:16 +05:00
valitovgaziz 594ef667bd modified: main_dc/.env
api_es add port
2025-11-17 21:40:55 +05:00
valitovgaziz ff2a2c0ddb modified: main_dc/yalarba/api_tp/internal/models/o_auth_provider.go
modified:   main_dc/yalarba/api_tp/internal/models/user.go
add foreignKey
2025-11-17 21:37:06 +05:00
valitovgaziz 7335eb5aa8 modified: main_dc/yalarba/api_tp/internal/handlers/auth.go
modified:   main_dc/yalarba/api_tp/internal/handlers/oauth.go
	modified:   main_dc/yalarba/api_tp/internal/handlers/oauth_yandex.go
	modified:   main_dc/yalarba/api_tp/internal/models/user.go
	modified:   main_dc/yalarba/api_tp/internal/repository/user_repository.go
	modified:   main_dc/yalarba/api_tp/internal/service/user_service.go
	modified:   main_dc/yalarba/api_tp/internal/utils/oauth_utils.go
	modified:   main_dc/yalarba/api_tp/pkg/database/postgres.go
change naming for user into api
2025-11-17 21:06:40 +05:00
valitovgaziz cbade7347d modified: main_dc/yalarba/easySite/easySite/app/pages/auth/login.vue
fix problem message on login page
2025-11-17 20:48:43 +05:00
valitovgaziz 0f06ef3219 modified: main_dc/yalarba/easySite/easySite/app/composables/useObjects.ts
modified:   main_dc/yalarba/easySite/easySite/app/plugins/theme.client.ts
delete files
2025-11-17 05:49:49 +05:00
valitovgaziz 2d29f7ecd9 modified: main_dc/yalarba/easySite/easySite/app/components/ImageGallery.vue
modified:   main_dc/yalarba/easySite/easySite/app/components/ObjectForm.vue
	modified:   main_dc/yalarba/easySite/easySite/app/components/layout/HamburgerMenu.vue
	modified:   main_dc/yalarba/easySite/easySite/app/components/layout/ObjectsNavigation.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/objects/[id]/index.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/objects/index.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/objects/my-objects.vue
fix problems
2025-11-16 04:15:15 +05:00
valitovgaziz 8b7ca0c120 modified: main_dc/yalarba/easySite/easySite/app/pages/profile/index.vue
remove problem messages with before or ofter
2025-11-16 03:40:42 +05:00
valitovgaziz 34859a804b modified: main_dc/yalarba/easySite/easySite/app/components/BookingModal.vue
modified:   main_dc/yalarba/easySite/easySite/app/components/ImageGallery.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/objects/[id]/edit.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/objects/[id]/index.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/profile/index.vue
	modified:   main_dc/yalarba/easySite/easySite/nuxt.config.ts
fix fix fix
2025-11-15 11:31:32 +05:00
valitovgaziz e26624935c modified: main_dc/yalarba/easySite/easySite/app/layouts/auth.vue
modified:   main_dc/yalarba/easySite/easySite/app/pages/auth/login.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/profile/edit.vue
fix fix fix
2025-11-15 11:08:48 +05:00
valitovgaziz 0fd4c2bb7e modified: main_dc/yalarba/easySite/easySite/app/pages/objects/index.vue
set aNum bNum to explicit type conversion number
2025-11-15 10:44:38 +05:00
valitovgaziz 54b203130e modified: main_dc/yalarba/easySite/easySite/app/pages/auth/login.vue
modified:   main_dc/yalarba/easySite/easySite/app/pages/auth/register.vue
	modified:   main_dc/yalarba/easySite/easySite/package-lock.json
	modified:   main_dc/yalarba/easySite/easySite/package.json
format input with linear break in register and login pages
2025-11-15 10:33:51 +05:00
valitovgaziz a3437eebb9 modified: main_dc/yalarba/easySite/easySite/app/pages/auth/register.vue
fix bag with incompatable genericObject in RegisterFrom <=> values
2025-11-15 08:11:07 +05:00
valitovgaziz 9ffe0c54f6 modified: main_dc/yalarba/easySite/easySite/app/pages/auth/login.vue
modified:   main_dc/yalarba/easySite/easySite/app/types/auth.ts
fix bag with incompatble genericObject LoginForm <=> values
2025-11-15 08:10:17 +05:00
valitovgaziz 22ecb2d8f4 modified: main_dc/yalarba/easySite/easySite/app/pages/index.vue
delete overheaded header and footer from main page
2025-11-15 07:45:37 +05:00
valitovgaziz 08723182ca modified: main_dc/yalarba/easySite/easySite/app/app.vue
modified:   main_dc/yalarba/easySite/easySite/app/pages/auth/login.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/auth/register.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/objects/[id]/edit.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/objects/[id]/index.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/objects/create.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/objects/index.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/objects/my-objects.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/profile/edit.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/profile/index.vue
	modified:   main_dc/yalarba/easySite/easySite/app/types/auth.ts
add default layout and delete doubled header footer
2025-11-15 06:38:19 +05:00
valitovgaziz d62cb606de modified: main_dc/yalarba/api_es/internal/database/psql_db.go
modified:   main_dc/yalarba/api_es/internal/models/object.go
add constrains and fix bag with import
2025-11-15 05:46:14 +05:00
valitovgaziz 5501581467 modified: main_dc/yalarba/api_es/internal/database/psql_db.go
Replace log to zapLogg
2025-11-15 05:42:06 +05:00
valitovgaziz c63480be25 new file: main_dc/yalarba/api_es/internal/database/feel_data.go
modified:   main_dc/yalarba/api_es/internal/database/psql_db.go
add to migrate Object and fill init data
2025-11-15 05:38:28 +05:00
valitovgaziz 39c871476b modified: main_dc/nginx/nginx-ssl.conf
modified:   main_dc/yalarba/easySite/easySite/nuxt.config.ts
new setting for nginx proxy cors
2025-11-15 05:09:23 +05:00
valitovgaziz a62c15fb53 modified: main_dc/nginx/nginx-ssl.conf
delete overhead cores from nginx config
2025-11-15 04:55:09 +05:00
valitovgaziz 5446bef3e9 modified: main_dc/nginx/nginx-ssl.conf
modified:   main_dc/yalarba/api_es/internal/router/setMiddleware.go
add headers nginx
2025-11-15 04:51:50 +05:00
valitovgaziz 2ac1c1574b modified: main_dc/yalarba/api_es/internal/router/setMiddleware.go
set new corse for api_es
2025-11-15 04:31:12 +05:00
valitovgaziz a22085ff08 modified: main_dc/yalarba/easySite/easySite/app/pages/auth/login.vue
modified:   main_dc/yalarba/easySite/easySite/app/pages/auth/register.vue
fix the dark theme for register and login pages
2025-11-15 02:19:12 +05:00
valitovgaziz 8fee46ce5c modified: main_dc/yalarba/easySite/easySite/app/composables/useAuth.ts
modified:   main_dc/yalarba/easySite/easySite/app/middleware/auth.ts
modified:   main_dc/yalarba/easySite/easySite/app/pages/auth/login.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/auth/register.vue
	new file:   main_dc/yalarba/easySite/easySite/app/pages/plugins/auth.client.ts
	modified:   main_dc/yalarba/easySite/easySite/app/pages/profile/index.vue
	new file:   main_dc/yalarba/easySite/easySite/app/schemas/auth.ts
modified:   main_dc/yalarba/easySite/easySite/app/types/auth.ts
	modified:   main_dc/yalarba/easySite/easySite/package-lock.json
	modified:   main_dc/yalarba/easySite/easySite/package.json
update login register with use vee and firebase
2025-11-14 23:55:02 +05:00
valitovgaziz e5723490d4 modified: main_dc/nginx/nginx-ssl.conf
modified:   main_dc/yalarba/easySite/easySite/app/pages/auth/register.vue
delete cors settings from nginx
2025-11-14 05:04:42 +05:00
valitovgaziz b324349b03 modified: main_dc/yalarba/easySite/easySite/app/pages/auth/register.vue
add to path for reqeust register /api/ ))))
2025-11-14 04:47:40 +05:00
valitovgaziz 19e9d85eff modified: main_dc/Makefile
delete clean command
2025-11-14 04:28:07 +05:00
valitovgaziz e5dc2ee778 modified: main_dc/yalarba/easySite/easySite/app/pages/auth/register.vue
fullNmae = first+last
2025-11-14 04:26:33 +05:00
valitovgaziz 3e022c84fe modified: main_dc/Makefile
modified:   main_dc/yalarba/easySite/easySite/nuxt.config.ts
add clean command to Makefile and preset: 'none" for ui
2025-11-14 04:12:48 +05:00
valitovgaziz 80f4c4d649 modified: main_dc/yalarba/easySite/easySite/nuxt.config.ts
false for ui:fonts fetch
2025-11-14 04:08:04 +05:00
valitovgaziz 76b2aae44c modified: main_dc/yalarba/easySite/easySite/app/pages/auth/register.vue
add first and last names for forms and change logic as
fullName=firstName+lastName
2025-11-14 04:01:11 +05:00
valitovgaziz 6b13e77bb6 modified: main_dc/yalarba/easySite/easySite/Dockerfile
easysite102.ru moove to FROM node:24.11.0-alpine version of node.js
2025-11-14 03:41:30 +05:00
valitovgaziz 25e8263f48 modified: main_dc/valitovgaziz/analytics/Dockerfile
modified:   main_dc/yalarba/easySite/easySite/nuxt.config.ts
change node.js version to FROM node:24.11.0-alpine for analytics
2025-11-14 03:35:00 +05:00
valitovgaziz 3e5608cdd8 modified: main_dc/nginx/nginx-ssl.conf
set path without api to http://api_es:8088/
2025-11-13 06:04:20 +05:00
valitovgaziz 9cdad0902f modified: main_dc/nginx/nginx-ssl.conf
modified:   main_dc/yalarba/easySite/easySite/app/pages/auth/register.vue
new register and set nginx-ssh.conf easysiste.ru api to 8088 port
2025-11-13 05:58:49 +05:00
valitovgaziz c5b80129d3 modified: main_dc/yalarba/api_es/internal/dto/user.go
modified:   main_dc/yalarba/api_es/internal/handler/user_handler.go
	modified:   main_dc/yalarba/api_es/internal/middleware/auth.go
	modified:   main_dc/yalarba/api_es/internal/router/router.go
set authorization with cooky and bearer jwt token
2025-11-13 04:37:15 +05:00
valitovgaziz bdcf6ad431 deleted: main_dc/yalarba/api_es/internal/handler/auth_handler.go
modified:   main_dc/yalarba/api_es/internal/handler/user_handler.go
	modified:   main_dc/yalarba/api_es/internal/middleware/auth.go
	modified:   main_dc/yalarba/api_es/internal/service/user_service.go
fix bag with secret key
2025-11-13 04:21:54 +05:00
valitovgaziz 092ba74691 modified: main_dc/docker-compose.yml
hardcode the port into test path request
2025-11-12 14:40:38 +05:00
valitovgaziz 074482279a modified: main_dc/docker-compose.yml
timeout 5s and interval 10s
2025-11-12 14:21:34 +05:00
valitovgaziz c28dfafdcd modified: main_dc/yalarba/api_es/go.mod
modified:   main_dc/yalarba/api_es/go.sum
	modified:   main_dc/yalarba/api_es/internal/router/router.go
	new file:   main_dc/yalarba/api_es/internal/router/setMiddleware.go
add middlewares, loggers, cors
2025-11-12 14:11:20 +05:00
valitovgaziz 03f974cbe5 modified: main_dc/docker-compose.yml
change check path
2025-11-12 13:53:18 +05:00
valitovgaziz 3b38d15a4b modified: main_dc/docker-compose.yml
modified:   main_dc/yalarba/api_es/.env
change port to variable from .env file
2025-11-12 13:25:32 +05:00
valitovgaziz e436647091 modified: main_dc/docker-compose.yml
modified:   main_dc/yalarba/api_es/internal/handler/all_handlers.go
	new file:   main_dc/yalarba/api_es/internal/handler/health.go
	modified:   main_dc/yalarba/api_es/internal/router/router.go
	new file:   main_dc/yalarba/api_es/internal/utils/formatTime.go
	new file:   main_dc/yalarba/api_es/internal/utils/response.go
	new file:   main_dc/yalarba/api_es/internal/utils/utils.go
	new file:   main_dc/yalarba/api_es/internal/utils/validation.go
add utils and health check heandlers into routes
2025-11-12 13:15:20 +05:00
valitovgaziz 980c23ecdf modified: main_dc/yalarba/api_es/.env
modified:   main_dc/yalarba/api_es/internal/config/config.go
set server port to 8088
2025-11-12 12:53:39 +05:00
valitovgaziz 0ae8ce7f45 modified: main_dc/yalarba/api_es/cmd/main.go
fix main api_es
2025-11-12 08:19:33 +05:00
valitovgaziz 032ce66865 modified: main_dc/yalarba/api_es/cmd/main.go
modified:   main_dc/yalarba/api_es/internal/handler/all_handlers.go
	modified:   main_dc/yalarba/api_es/internal/handler/user_handler.go
	new file:   main_dc/yalarba/api_es/internal/router/router.go
add rounter, logger router
2025-11-12 05:59:14 +05:00
valitovgaziz 1c74d12df6 modified: main_dc/yalarba/api_es/go.mod
modified:   main_dc/yalarba/api_es/go.sum
	new file:   main_dc/yalarba/api_es/internal/dto/user.go
	new file:   main_dc/yalarba/api_es/internal/handler/all_handlers.go
	new file:   main_dc/yalarba/api_es/internal/handler/auth_handler.go
	new file:   main_dc/yalarba/api_es/internal/handler/user_handler.go
	deleted:    main_dc/yalarba/api_es/internal/handlers/all_handlers.go
	deleted:    main_dc/yalarba/api_es/internal/handlers/auth_handler.go
	deleted:    main_dc/yalarba/api_es/internal/handlers/user_handler.go
	new file:   main_dc/yalarba/api_es/internal/middleware/auth.go
	deleted:    main_dc/yalarba/api_es/internal/repositories/user_repository.go
	new file:   main_dc/yalarba/api_es/internal/repository/user_repository.go
	new file:   main_dc/yalarba/api_es/internal/service/user_service.go
	new file:   main_dc/yalarba/api_es/internal/utils/jwt.go
add service, handler, repository for user model
2025-11-12 05:19:26 +05:00
valitovgaziz e30d62fc5a modified: main_dc/yalarba/api_es/internal/models/user.go
add default for fullname, first and last names
2025-11-12 04:56:06 +05:00
valitovgaziz 15d8faf53e modified: main_dc/yalarba/api_es/internal/models/user.go
add full name to user model
2025-11-12 04:41:30 +05:00
valitovgaziz d08b94dbb0 new file: main_dc/yalarba/api_es/internal/handlers/all_handlers.go
new file:   main_dc/yalarba/api_es/internal/handlers/auth_handler.go
	new file:   main_dc/yalarba/api_es/internal/handlers/user_handler.go
	new file:   main_dc/yalarba/api_es/internal/models/busines_object.go
	new file:   main_dc/yalarba/api_es/internal/models/news.go
	new file:   main_dc/yalarba/api_es/internal/models/reports.go
	modified:   main_dc/yalarba/api_es/internal/models/user.go
	new file:   main_dc/yalarba/api_es/internal/repositories/user_repository.go
add empty files
2025-11-12 04:38:43 +05:00
valitovgaziz eedcabe7bf modified: main_dc/docker-compose.yml
depends on health check all
2025-11-11 11:46:18 +05:00
valitovgaziz da0279d9b6 modified: main_dc/docker-compose.yml
add health check for nginx
2025-11-11 11:41:42 +05:00
valitovgaziz 9898c95f6c modified: main_dc/Makefile
add wn commands watch -n 2 docker ps
2025-11-11 11:39:13 +05:00
valitovgaziz 53580557c7 modified: main_dc/Makefile
add commands watch -n 2
2025-11-11 11:37:07 +05:00
valitovgaziz 4cd4afaf7f modified: main_dc/Makefile
add commands for certbot
2025-11-11 11:34:04 +05:00
valitovgaziz f082d0ee37 modified: main_dc/docker-compose.yml
add health check for certbot
2025-11-11 11:30:30 +05:00
valitovgaziz 9b3d0a5fe9 modified: main_dc/Makefile
add restart command for analytics
2025-11-11 10:47:20 +05:00
valitovgaziz 5d1acac4fa modified: main_dc/docker-compose.yml
change health check to cmd wget --spider
2025-11-11 10:44:43 +05:00
valitovgaziz 3fab5815ec modified: main_dc/docker-compose.yml
change curl to wget --spider health check for easysite
2025-11-11 10:28:36 +05:00
valitovgaziz 85baf51a53 modified: main_dc/yalarba/easySite/easySite/server/api/health.get.ts
set an ok status
2025-11-11 09:26:24 +05:00
valitovgaziz c36a49c36f modified: main_dc/yalarba/easySite/easySite/server/api/health.get.ts
set response status 200
2025-11-11 09:16:26 +05:00
valitovgaziz 77a64fbfb7 modified: main_dc/docker-compose.yml
set health check path
2025-11-11 08:57:57 +05:00
valitovgaziz 29fab22f83 modified: main_dc/Makefile
add es start command docker ps
2025-11-11 06:19:56 +05:00
valitovgaziz 5781601def new file: main_dc/yalarba/easySite/easySite/server/api/health.get.ts
add health check endpoint
2025-11-11 06:18:53 +05:00
valitovgaziz c959e11a5e modified: main_dc/Makefile
fix command for api_es
2025-11-11 06:14:29 +05:00
valitovgaziz f626a86884 modified: main_dc/Makefile
add commands for api_es
2025-11-11 06:03:04 +05:00
valitovgaziz ed355ee60d modified: main_dc/docker-compose.yml
modified:   main_dc/yalarba/api_es/.env
	modified:   main_dc/yalarba/api_es/cmd/main.go
	modified:   main_dc/yalarba/api_es/go.mod
	modified:   main_dc/yalarba/api_es/go.sum
	new file:   main_dc/yalarba/api_es/internal/config/config.go
	new file:   main_dc/yalarba/api_es/internal/database/psql_db.go
	new file:   main_dc/yalarba/api_es/pkg/logger/helpers.go
	new file:   main_dc/yalarba/api_es/pkg/logger/interface.go
	new file:   main_dc/yalarba/api_es/pkg/logger/logger.go
	new file:   main_dc/yalarba/api_es/pkg/logger/route_logger.go
add new User model for api_es
add global zapLogger api_es
add configs dotenv api_es
sipmplify main api_es
2025-11-11 05:58:36 +05:00
valitovgaziz 510d17fc25 modified: main_dc/Makefile
fix command
2025-11-11 03:56:38 +05:00
valitovgaziz d81e8cf2c8 modified: main_dc/nginx/nginx-ssl.conf
modified:   main_dc/valitovgaziz/analytics/server.js
simplyfy nginx-ssl.conf and remove api from paths in server.js
2025-11-11 03:55:19 +05:00
valitovgaziz 9fe2e24189 modified: main_dc/nginx/nginx-ssl.conf
set new settings for valitovgaziz.ru site api/analytitcs
2025-11-11 03:47:03 +05:00
valitovgaziz 5d2157fe06 modified: main_dc/Makefile
add commands for analytics valitovgaziz site
2025-11-11 03:38:48 +05:00
valitovgaziz 32385eb549 modified: main_dc/valitovgaziz/analytics/package.json
modified:   main_dc/valitovgaziz/analytics/server.js
change dependencies
2025-11-11 03:32:56 +05:00
valitovgaziz 5b10be0113 modified: main_dc/valitovgaziz/analytics/Dockerfile
add copy package.json
2025-11-11 03:29:55 +05:00
valitovgaziz 7436b401ff modified: main_dc/docker-compose.yml
fix path for analitics
2025-11-11 03:24:30 +05:00
valitovgaziz a013fcacd8 modified: main_dc/docker-compose.yml
modified:   main_dc/nginx/nginx-ssl.conf
	new file:   main_dc/valitovgaziz/analytics/Dockerfile
	new file:   main_dc/valitovgaziz/analytics/package.json
	new file:   main_dc/valitovgaziz/analytics/server.js
	new file:   main_dc/valitovgaziz/html/JavaScript/analytics.js
	modified:   main_dc/valitovgaziz/html/index.html
add nginx settings for api logs for valitovgaziz.ru site,
add container for metrica container,
add metrica scripts on site valitovgaziz.ru
2025-11-11 03:22:14 +05:00
valitovgaziz ea9540dc73 new file: main_dc/yalarba/api_es/internal/models/authentication.go
new file:   main_dc/yalarba/api_es/internal/models/filter.go
	new file:   main_dc/yalarba/api_es/internal/models/object.go
	deleted:    main_dc/yalarba/api_es/internal/models/rest_object.go
	new file:   main_dc/yalarba/api_es/internal/models/review.go
	modified:   main_dc/yalarba/api_es/internal/models/user.go
add models into es system
2025-11-11 02:25:22 +05:00
valitovgaziz 2dbf2c557e new file: main_dc/yalarba/api_es/internal/models/rest_object.go
create rest_object
2025-11-10 22:40:44 +05:00
valitovgaziz 593840ec28 new file: main_dc/yalarba/api_es/internal/models/user.go
modified:   main_dc/yalarba/api_tp/internal/models/contacts.go
modifay
2025-11-10 21:50:15 +05:00
valitovgaziz c5b3d795b9 modified: main_dc/valitovgaziz/html/index.html
enterprener and full stack developer header of valitovgaziz.ru site
2025-11-09 14:13:13 +05:00
valitovgaziz bd4c94b112 modified: main_dc/valitovgaziz/html/digital_background.js
modified:   main_dc/valitovgaziz/html/style.css
	modified:   main_dc/valitovgaziz/html/style/digital_background.css
add workible digital background
2025-11-09 06:29:29 +05:00
valitovgaziz f60ad0d3d8 new file: main_dc/valitovgaziz/html/digital_background.js
modified:   main_dc/valitovgaziz/html/index.html
	modified:   main_dc/valitovgaziz/html/style/digital_background.css
try add backgroud digital background
2025-11-09 06:13:44 +05:00
valitovgaziz 367fcbfe0d modified: main_dc/valitovgaziz/html/style.css
new file:   main_dc/valitovgaziz/html/style/digital_background.css
add new backgound colors next set digital backgournd
2025-11-09 06:05:37 +05:00
valitovgaziz 4dee4b17b9 modified: main_dc/valitovgaziz/html/darkTheme.css
modified:   main_dc/valitovgaziz/html/index.html
	modified:   main_dc/valitovgaziz/html/style.css
add smooth transition for all and system preferences
2025-11-09 05:29:07 +05:00
valitovgaziz 39861816f5 modified: main_dc/valitovgaziz/html/scripts.js
add universal handler for button sendMessageTelegram
2025-11-09 05:18:40 +05:00
valitovgaziz 9dd9d8b9ed modified: main_dc/valitovgaziz/html/index.html
change Analitics description
2025-11-09 05:08:38 +05:00
valitovgaziz 8ab5a65ce6 modified: main_dc/valitovgaziz/html/style/links_style.css
repository links color fix
2025-11-09 05:03:20 +05:00
valitovgaziz 3e44457151 modified: main_dc/valitovgaziz/html/index.html
prettify skill section valiotvgaziz.ru site
2025-11-09 03:07:32 +05:00
valitovgaziz 7ec3a7f048 modified: main_dc/valitovgaziz/html/style.css
modified:   main_dc/valitovgaziz/html/style/hero_section.css
prettify header valitovgaziz.ru site
2025-11-09 03:03:32 +05:00
valitovgaziz acf18fa765 modified: main_dc/valitovgaziz/html/index.html
modified:   main_dc/yalarba/easySite/easySite/app/pages/profile/edit.vue
footer add into create by
2025-11-09 01:56:44 +05:00
valitovgaziz edf853e998 modified: main_dc/valitovgaziz/html/index.html
fix more mistatkes into valitovagaziz.ru site
2025-11-08 11:59:53 +05:00
valitovgaziz 8317bf44d5 modified: main_dc/valitovgaziz/html/index.html
modified:   main_dc/valitovgaziz/html/scripts.js
fix mistakes into valitovgaziz site
2025-11-08 11:45:55 +05:00
valitovgaziz d757343901 modified: main_dc/valitovgaziz/html/index.html
fix mistakes
2025-11-08 11:24:03 +05:00
valitovgaziz 96fb49dd89 modified: main_dc/valitovgaziz/html/index.html
modified:   main_dc/valitovgaziz/html/style.css
fix description and skills box behevior
2025-11-08 11:17:56 +05:00
valitovgaziz cea4c2484a modified: main_dc/yalarba/easySite/easySite/app/pages/objects/index.vue
cursor pointer
2025-11-07 20:10:34 +05:00
valitovgaziz 36876e620b modified: main_dc/yalarba/easySite/easySite/app/pages/objects/index.vue
allObjects page set --text-secondary color for text
2025-11-07 15:18:31 +05:00
valitovgaziz aa5338d71b modified: main_dc/yalarba/easySite/easySite/app/pages/objects/my-objects.vue
fix bag with scroll table, delete table set boxes, verical scroll
2025-11-07 15:12:17 +05:00
valitovgaziz f78a23ff38 modified: main_dc/yalarba/easySite/easySite/app/pages/objects/[id]/edit.vue
modified:   main_dc/yalarba/easySite/easySite/app/pages/objects/[id]/index.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/objects/create.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/objects/index.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/objects/my-objects.vue
add header footer components into new pages
2025-11-07 14:36:10 +05:00
valitovgaziz be08bfed7e new file: main_dc/yalarba/easySite/easySite/app/components/BookingModal.vue
new file:   main_dc/yalarba/easySite/easySite/app/components/ImageGallery.vue
	new file:   main_dc/yalarba/easySite/easySite/app/components/layout/ObjectsNavigation.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/objects/[id]/edit.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/objects/[id]/index.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/objects/create.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/objects/index.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/objects/my-objects.vue
upgrade UI/UX
2025-11-07 14:25:38 +05:00
valitovgaziz 1ba3f907ac new file: main_dc/yalarba/easySite/easySite/app/components/ObjectCard.vue
new file:   main_dc/yalarba/easySite/easySite/app/components/ObjectForm.vue
	deleted:    main_dc/yalarba/easySite/easySite/app/components/forms/ObjectForm.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/objects/[id]/edit.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/objects/[id]/index.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/objects/create.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/objects/index.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/objects/my-objects.vue
	deleted:    main_dc/yalarba/easySite/easySite/app/pages/objects/search.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/profile/edit.vue
add pages for object, objectsSearch, editObject, createObject
2025-11-07 13:09:30 +05:00
valitovgaziz e8981bbd45 modified: main_dc/yalarba/easySite/easySite/app/components/layout/HamburgerMenu.vue
modified:   main_dc/yalarba/easySite/easySite/app/pages/objects/[id]/index.vue
set path for NuxtLink links to contact and support pages
2025-11-07 09:07:45 +05:00
valitovgaziz 430c9faa64 modified: main_dc/yalarba/easySite/easySite/app/pages/profile/edit.vue
add edit profile page (update)
2025-11-07 08:27:41 +05:00
valitovgaziz 9e8d1c8418 modified: main_dc/yalarba/api_es/go.mod
modified:   main_dc/yalarba/api_es/go.sum
go mod tidy
2025-11-06 20:04:29 +05:00
valitovgaziz 911fe9d24b modified: main_dc/yalarba/easySite/easySite/app/pages/profile/index.vue
add user info to profile page easysite102.ru site
2025-11-06 05:33:31 +05:00
valitovgaziz 655a427855 modified: main_dc/valitovgaziz/html/index.html
set java to beginner and golang on upper-intermediat
2025-11-05 23:52:38 +05:00
valitovgaziz 73f6edbfc1 modified: main_dc/valitovgaziz/html/index.html
fix text into yalarba-content section
2025-11-05 23:46:04 +05:00
valitovgaziz 5054bbac19 modified: main_dc/valitovgaziz/html/index.html
modified:   main_dc/valitovgaziz/html/style/social_link.css
prettyfay
2025-11-05 23:43:32 +05:00
valitovgaziz 5159b3f0ad modified: main_dc/valitovgaziz/html/index.html
modified:   main_dc/valitovgaziz/html/style/social_link.css
round the link into head of valitovgaziz.ru site
2025-11-05 23:41:50 +05:00
valitovgaziz 64abaf1265 modified: main_dc/valitovgaziz/html/index.html
modified:   main_dc/valitovgaziz/html/style.css
	new file:   main_dc/valitovgaziz/html/style/skill_section.css
change skills section valitovgaziz.ru site
2025-11-05 16:50:14 +05:00
valitovgaziz 9d1c829d70 modified: main_dc/valitovgaziz/html/style.css
change color style for light theme
2025-11-05 16:26:53 +05:00
valitovgaziz 97e3831246 modified: main_dc/valitovgaziz/html/index.html
delete one line into header, valitovgaziz.ru site
2025-11-05 13:50:22 +05:00
valitovgaziz 024071b4f2 modified: main_dc/valitovgaziz/html/index.html
add to links  target="_blank"
2025-11-05 13:45:56 +05:00
valitovgaziz 0f64a2a9d7 modified: main_dc/valitovgaziz/html/index.html
modified:   main_dc/valitovgaziz/html/style.css
	new file:   main_dc/valitovgaziz/html/style/links_style.css
add style file for links
2025-11-05 13:41:44 +05:00
valitovgaziz 636875b336 new file: main_dc/valitovgaziz/html/images/favicon/brand-telegram.svg
new file:   main_dc/valitovgaziz/html/images/favicon/brand-vk.svg
	modified:   main_dc/valitovgaziz/html/index.html
valitovgaziz site header target=_blank
2025-11-05 13:27:57 +05:00
valitovgaziz c0a00a4237 modified: main_dc/valitovgaziz/html/index.html
change yalarba-stats for beter and true perfomance
2025-11-05 11:40:37 +05:00
valitovgaziz f7a18aa68d modified: main_dc/valitovgaziz/html/index.html
add key word into valitovgaziz site
2025-11-05 11:20:53 +05:00
valitovgaziz e803e5d3cc modified: main_dc/valitovgaziz/html/index.html
modified:   main_dc/valitovgaziz/html/style/repository_section.css
motivation text
2025-11-05 11:15:00 +05:00
valitovgaziz 48775d0d84 modified: main_dc/valitovgaziz/html/index.html
modified:   main_dc/valitovgaziz/html/style.css
	modified:   main_dc/valitovgaziz/html/style/about.css
	modified:   main_dc/valitovgaziz/html/style/repository_section.css
fix height of repository section valitovgaziz.ru site`
2025-11-05 11:06:39 +05:00
valitovgaziz a87ad669bf modified: main_dc/valitovgaziz/html/style.css
modified:   main_dc/valitovgaziz/html/style/about.css
styled photo into valitovgaziz.ru site
2025-11-05 10:56:29 +05:00
valitovgaziz 67ea38d24b modified: main_dc/valitovgaziz/html/style.css
fix footer for adapt to phone
2025-11-05 06:36:07 +05:00
valitovgaziz 03cfa84c5b modified: main_dc/valitovgaziz/html/style.css
modified:   main_dc/valitovgaziz/html/style/yalarba_investmen.css
fix skill tag overflow hidden
2025-11-05 06:26:44 +05:00
valitovgaziz feae5af46a modified: main_dc/valitovgaziz/html/index.html
modified:   main_dc/valitovgaziz/html/style.css
fix footer valitovgaziz.ru site
2025-11-05 06:19:19 +05:00
valitovgaziz 83ecb15c81 modified: main_dc/valitovgaziz/html/darkTheme.css
modified:   main_dc/valitovgaziz/html/style.css
	modified:   main_dc/valitovgaziz/html/style/about.css
	new file:   main_dc/valitovgaziz/html/style/repository_section.css
about section style photo prettify
2025-11-05 05:25:08 +05:00
valitovgaziz 8bfeb68a89 modified: main_dc/valitovgaziz/html/index.html
modified:   main_dc/valitovgaziz/html/style/footer.css
change footer flow in valitovgaziz.ru site
2025-11-05 04:27:23 +05:00
valitovgaziz 3819486119 modified: main_dc/BB/bbvue/public/images/slide2.jpg
modified:   main_dc/BB/bbvue/public/images/slide3.jpg
	modified:   main_dc/BB/bbvue/public/images/slide4.jpg
	new file:   main_dc/BB/bbvue/public/images/slide44.jpg
add new photo to sider BB
2025-11-04 03:26:01 +05:00
valitovgaziz 9bb1690140 new file: main_dc/BB/bbvue/public/images/slide2.jpg
new file:   main_dc/BB/bbvue/public/images/slide3.jpg
	new file:   main_dc/BB/bbvue/public/images/slide4.jpg
	modified:   main_dc/BB/bbvue/src/views/Home.vue
add slider, new slides, and adaptation for all phone sizes
2025-11-04 02:59:12 +05:00
valitovgaziz 46e804cc90 modified: main_dc/Makefile
fix command for rebuild bbvue
2025-11-04 02:14:36 +05:00
valitovgaziz a95ad3cfdc new file: htmlCssJS_Poligon/index.html
modified:   main_dc/BB/bbvue/src/views/PersonalBests.vue
	modified:   main_dc/valitovgaziz/html/style.css
fix bag with not created personal bests
2025-11-03 13:22:42 +05:00
valitovgaziz 88f032c664 modified: main_dc/yalarba/easySite/easySite/app/components/layout/Header.vue
delete empty line
2025-11-02 15:07:17 +05:00
valitovgaziz 6bad35acca modified: main_dc/yalarba/easySite/easySite/app/components/layout/Footer.vue
modified:   main_dc/yalarba/easySite/easySite/app/pages/support/index.vue
add support page and set Footer link to support page
2025-11-02 14:56:19 +05:00
valitovgaziz 2c674137fe modified: main_dc/yalarba/easySite/easySite/app/components/layout/PlanBadge.vue
set for planBage preview message that revile on hover
Added a message to the rate badge that appears when you hover
2025-11-02 14:26:09 +05:00
valitovgaziz a35373a7e3 modified: main_dc/valitovgaziz/html/index.html
new file:   main_dc/yalarba/api_tp/internal/models/contacts.go
	new file:   main_dc/yalarba/easySite/easySite/app/assets/images/itFestOren.jpg
	modified:   main_dc/yalarba/easySite/easySite/app/pages/objects/index.vue
easysite102.ru add new page contacts
valitovgaziz.ru add text about motivation
2025-11-02 13:59:42 +05:00
valitovgaziz f44b5f38d8 modified: main_dc/yalarba/easySite/easySite/app/pages/index.vue
change main page easysite102.ru
2025-11-02 05:24:44 +05:00
valitovgaziz ebc3dd954c modified: main_dc/Makefile
add htop command into Makefile
2025-11-02 05:14:05 +05:00
valitovgaziz 2b0b19e515 modified: main_dc/yalarba/easySite/easySite/app/components/layout/Footer.vue
modified:   main_dc/yalarba/easySite/easySite/app/components/layout/Header.vue
	new file:   main_dc/yalarba/easySite/easySite/app/components/layout/PlanBadge.vue
	new file:   main_dc/yalarba/easySite/easySite/app/components/layout/pricing.vue
	new file:   main_dc/yalarba/easySite/easySite/app/components/pricing/FAQSection.vue
	new file:   main_dc/yalarba/easySite/easySite/app/components/pricing/PricingSection.vue
	new file:   main_dc/yalarba/easySite/easySite/app/pages/rules/index.vue
add plane into footer header as a bage and link, add page for tarif
palanes
2025-11-02 05:11:08 +05:00
valitovgaziz 50e8c90528 modified: main_dc/yalarba/easySite/easySite/app/components/layout/Footer.vue
modified:   main_dc/yalarba/easySite/easySite/nuxt.config.ts
sitemap.xml page added into main page easysite102.ru
2025-11-02 03:05:26 +05:00
valitovgaziz 788583af14 modified: main_dc/yalarba/easySite/easySite/app/components/layout/Footer.vue
change date from 2024 to 2025 year on main page
2025-11-01 23:44:51 +05:00
valitovgaziz 67455fba6c modified: main_dc/yalarba/easySite/easySite/app/components/layout/Footer.vue
new file:   main_dc/yalarba/easySite/easySite/app/pages/news/index.vue
add news page into easysite102.ru
2025-11-01 23:43:04 +05:00
valitovgaziz ab4e42c5e9 modified: main_dc/yalarba/easySite/easySite/Dockerfile
RUN npm install --production line from RUN npm ci
shorterner time building
2025-11-01 23:24:53 +05:00
valitovgaziz 48257d86dc modified: main_dc/yalarba/easySite/easySite/app/components/layout/Footer.vue
new file:   main_dc/yalarba/easySite/easySite/app/pages/partner/index.vue
add partners page
2025-11-01 23:22:05 +05:00
valitovgaziz 8f0905e2cc modified: main_dc/yalarba/easySite/easySite/app/components/layout/Footer.vue
add hover into footer for titels
2025-11-01 23:01:03 +05:00
valitovgaziz 0ddfff8732 modified: main_dc/yalarba/easySite/easySite/app/components/layout/Footer.vue
modified:   main_dc/yalarba/easySite/easySite/app/pages/contact/index.vue
	new file:   main_dc/yalarba/easySite/easySite/app/pages/requisites/index.vue
add contacts page and link from footer to contact page
2025-11-01 22:23:24 +05:00
valitovgaziz 69ef4a1918 deleted: main_dc/yalarba/easySite/.env
modified:   main_dc/yalarba/easySite/easySite/app/components/layout/Footer.vue
	new file:   main_dc/yalarba/easySite/easySite/app/pages/contact/index.vue
	modified:   main_dc/yalarba/easySite/easySite/nuxt.config.ts
	new file:   main_dc/yalarba/easySite/easySite/server/api/__sitemap__/blog-posts.get.ts
	new file:   main_dc/yalarba/easySite/easySite/server/api/__sitemap__/urls.get.ts
add contact page and setd message from contact page
2025-11-01 21:52:07 +05:00
valitovgaziz f032836fb2 modified: main_dc/yalarba/easySite/easySite/app/assets/css/travel.css
modified:   main_dc/yalarba/easySite/easySite/app/components/layout/Careers.vue
fix bag with overflow, it mast be pointer-events: none for before item
2025-11-01 15:21:42 +05:00
valitovgaziz f606faa083 modified: main_dc/yalarba/easySite/easySite/app/components/layout/Careers.vue
fix ansmooth behavior in scroll
2025-11-01 12:46:03 +05:00
valitovgaziz ff16cfd0f6 modified: main_dc/yalarba/easySite/easySite/app/assets/css/travel.css
modified:   main_dc/yalarba/easySite/easySite/app/components/layout/Careers.vue
fix bag with non working button in head of vacations page easysite102.ru
2025-11-01 12:14:28 +05:00
valitovgaziz 738b48eabd modified: main_dc/yalarba/easySite/easySite/app/components/layout/Careers.vue
modified:   main_dc/yalarba/easySite/easySite/app/components/layout/default.vue
	modified:   main_dc/yalarba/easySite/easySite/app/composables/useTelegram.ts
add telegram bot into vacations page easysite102.ru
2025-11-01 06:17:47 +05:00
valitovgaziz dcec3c3d32 modified: main_dc/yalarba/easySite/.env
modified:   main_dc/yalarba/easySite/easySite/app/components/layout/Careers.vue
	new file:   main_dc/yalarba/easySite/easySite/app/components/layout/default.vue
	new file:   main_dc/yalarba/easySite/easySite/app/composables/useTelegram.ts
	modified:   main_dc/yalarba/easySite/easySite/nuxt.config.ts
send mesage with ui from vocations page easysite
2025-11-01 06:06:47 +05:00
valitovgaziz fbfcb869ed modified: main_dc/yalarba/easySite/easySite/app/components/layout/Footer.vue
fix the footer
2025-11-01 04:47:38 +05:00
valitovgaziz 86936baa2d modified: main_dc/yalarba/easySite/easySite/app/components/layout/Careers.vue
add contact into mailto form-group
2025-11-01 04:40:52 +05:00
valitovgaziz 511dc100a9 modified: main_dc/yalarba/easySite/easySite/app/components/layout/Careers.vue
modified:   main_dc/yalarba/easySite/easySite/app/components/layout/Footer.vue
add emogy into Open Vocation link in footer layout
2025-11-01 04:32:50 +05:00
valitovgaziz 0b53761192 new file: main_dc/yalarba/easySite/easySite/app/components/layout/Careers.vue
modified:   main_dc/yalarba/easySite/easySite/app/components/layout/Footer.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/about/index.vue
	new file:   main_dc/yalarba/easySite/easySite/app/pages/vacations/index.vue
add careers page and link from footer to career
2025-11-01 02:34:41 +05:00
valitovgaziz 018e81f6a1 modified: main_dc/yalarba/easySite/easySite/nuxt.config.ts
modified:   main_dc/yalarba/easySite/easySite/package-lock.json
	modified:   main_dc/yalarba/easySite/easySite/package.json
install sitemaps
2025-10-31 13:42:50 +05:00
valitovgaziz 7ce67f3d8c modified: main_dc/yalarba/easySite/easySite/app/components/layout/Footer.vue
new file:   main_dc/yalarba/easySite/easySite/app/components/layout/PrivacyPolacy.vue
	new file:   main_dc/yalarba/easySite/easySite/app/components/layout/userAgreement.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/about/index.vue
	new file:   main_dc/yalarba/easySite/easySite/app/pages/agreements/privacy.vue
	new file:   main_dc/yalarba/easySite/easySite/app/pages/agreements/userAgreement.vue
	new file:   main_dc/yalarba/easySite/easySite/public/documents/privacyPolacy.pdf
	new file:   main_dc/yalarba/easySite/easySite/public/documents/userAgreement.pdf
add documents agreements user and privacy polacy
2025-10-31 13:34:15 +05:00
valitovgaziz eb3f9f64d8 deleted: main_dc/yalarba/easySite/easySite/app.config.ts
modified:   main_dc/yalarba/easySite/easySite/app/pages/about/index.vue
	new file:   main_dc/yalarba/easySite/easySite/public/images/itFestOren.jpg
set all cofigs into nuxt.config.ts
2025-10-31 11:40:21 +05:00
valitovgaziz c56d720d0c modified: main_dc/valitovgaziz/html/index.html
change description about my state now and what i am doing
2025-10-31 10:35:24 +05:00
valitovgaziz efdd202d87 modified: main_dc/yalarba/easySite/easySite/app.config.ts
modified:   main_dc/yalarba/easySite/easySite/app/pages/about/index.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/index.vue
	modified:   main_dc/yalarba/easySite/easySite/nuxt.config.ts
	new file:   main_dc/yalarba/easySite/easySite/public/logoIconES.svg
add metategs and keywords for main page
2025-10-31 08:59:59 +05:00
valitovgaziz 842979e0b9 modified: main_dc/yalarba/easySite/easySite/app/components/layout/Header.vue
modified:   main_dc/yalarba/easySite/easySite/nuxt.config.ts
	new file:   main_dc/yalarba/easySite/easySite/public/logoIcon16.png
	new file:   main_dc/yalarba/easySite/easySite/public/logoIcon180.png
	new file:   main_dc/yalarba/easySite/easySite/public/logoIcon32.png
	new file:   main_dc/yalarba/easySite/easySite/public/logoIcon32.svg
	new file:   main_dc/yalarba/easySite/easySite/public/logoIconR.svg
add svg favicon into easysite102.ru rund logoIcon
2025-10-31 05:29:24 +05:00
valitovgaziz 2bf7ee22ef new file: main_dc/yalarba/easySite/easySite/app.config.ts
modified:   main_dc/yalarba/easySite/easySite/app/pages/auth/login.vue
	modified:   main_dc/yalarba/easySite/easySite/nuxt.config.ts
	deleted:    main_dc/yalarba/easySite/easySite/public/favicon.ico
	new file:   main_dc/yalarba/easySite/easySite/public/logoIcon.png
add favicone logoIcon.png
2025-10-31 01:02:12 +05:00
valitovgaziz 924d88d724 modified: main_dc/yalarba/easySite/easySite/app/pages/index.vue
add link on main page of easysite102.ru
2025-10-30 11:55:56 +05:00
valitovgaziz e19bf32ac4 modified: main_dc/yalarba/easySite/easySite/app/composables/useTheme.ts
modified:   main_dc/yalarba/easySite/easySite/app/pages/index.vue
add call to action card words
2025-10-30 07:49:47 +05:00
valitovgaziz 3a138924c3 modified: main_dc/yalarba/easySite/easySite/app/components/layout/Header.vue
modified:   main_dc/yalarba/easySite/easySite/app/pages/index.vue
set main page tite
2025-10-30 06:01:31 +05:00
valitovgaziz 0a45141f1f modified: main_dc/yalarba/easySite/easySite/Dockerfile
revert to one stage build from multystage
2025-10-30 05:18:25 +05:00
valitovgaziz e00a5659aa modified: main_dc/yalarba/easySite/easySite/nuxt.config.ts
change nuxt.config.ts for remove in build stage reqest to fonts
2025-10-30 05:06:03 +05:00
valitovgaziz f36d3c6596 modified: main_dc/yalarba/easySite/easySite/app/components/layout/HamburgerMenu.vue
modified:   main_dc/yalarba/easySite/easySite/app/components/layout/Header.vue
	new file:   main_dc/yalarba/easySite/easySite/app/pages/about/index.vue
	modified:   main_dc/yalarba/easySite/easySite/nuxt.config.ts
add about page and navigation throught hambMenu
2025-10-30 04:48:11 +05:00
valitovgaziz 00909766a6 modified: main_dc/yalarba/easySite/easySite/Dockerfile
multystage builder for easysite102.ru
2025-10-30 04:24:52 +05:00
valitovgaziz f42f1ea933 deleted: main_dc/yalarba/easySite/easySite/app/assets/css/ffonts.css
delete ald file ffonts.css
2025-10-30 04:18:02 +05:00
valitovgaziz eb44f3efbd modified: main_dc/yalarba/easySite/easySite/app/assets/css/fonts.css
new file:   main_dc/yalarba/easySite/easySite/app/components/layout/HamburgerMenu.vue
	modified:   main_dc/yalarba/easySite/easySite/app/components/layout/Header.vue
	modified:   main_dc/yalarba/easySite/easySite/app/composables/useTheme.ts
	new file:   main_dc/yalarba/easySite/easySite/public/fonts/Lora-Regular.woff2
	new file:   main_dc/yalarba/easySite/easySite/public/fonts/Manrope-ExtraLight.woff2
	new file:   main_dc/yalarba/easySite/easySite/public/fonts/SourceSansPro-Regular.woff2
add fonts theme and add humbergerMenu
2025-10-30 04:08:00 +05:00
valitovgaziz b168ec42c2 new file: main_dc/yalarba/easySite/easySite/app/assets/css/ffonts.css
modified:   main_dc/yalarba/easySite/easySite/app/assets/css/fonts.css
	new file:   main_dc/yalarba/easySite/easySite/public/fonts/Inter-Regular.woff2
	new file:   main_dc/yalarba/easySite/easySite/public/fonts/JetBrainsMono-Regular.woff2
	new file:   main_dc/yalarba/easySite/easySite/public/fonts/PlayfairDisplay-Regular.woff2
add new fonts
2025-10-30 03:22:46 +05:00
valitovgaziz beecbb3b86 modified: main_dc/yalarba/easySite/easySite/app/assets/css/components.css
modified:   main_dc/yalarba/easySite/easySite/app/assets/css/main.css
	modified:   main_dc/yalarba/easySite/easySite/app/assets/css/travel.css
	modified:   main_dc/yalarba/easySite/easySite/app/assets/css/typography.css
	modified:   main_dc/yalarba/easySite/easySite/app/assets/css/variables.css
	modified:   main_dc/yalarba/easySite/easySite/app/components/layout/Footer.vue
	modified:   main_dc/yalarba/easySite/easySite/app/components/layout/Header.vue
	new file:   main_dc/yalarba/easySite/easySite/app/composables/useTheme.ts
	modified:   main_dc/yalarba/easySite/easySite/app/pages/index.vue
	new file:   main_dc/yalarba/easySite/easySite/app/plugins/theme.client.ts
add dark theme for easysite
2025-10-30 00:22:00 +05:00
valitovgaziz c4f8ed467a modified: main_dc/yalarba/easySite/easySite/app/pages/index.vue
change main page into easysite102.ru
2025-10-29 11:22:21 +05:00
valitovgaziz 79d58a237f modified: main_dc/Makefile
fix nginx all rebuild path for make nginx command
2025-10-29 06:29:24 +05:00
valitovgaziz 1221ae2e47 modified: main_dc/yalarba/api_es/cmd/main.go
delete oAuth
2025-10-29 06:24:22 +05:00
valitovgaziz 781c16d17a modified: main_dc/yalarba/api_es/Dockerfile
fix path to main.go and set new path
2025-10-29 06:21:37 +05:00
valitovgaziz f0afeed31d modified: main_dc/docker-compose.yml
modified:   main_dc/nginx/nginx-ssl.conf
	modified:   main_dc/yalarba/api_es/cmd/main.go
fix some names for path into api_es
2025-10-29 06:17:43 +05:00
valitovgaziz d45d99517c modified: main_dc/docker-compose.yml
modified:   main_dc/nginx/nginx-ssl.conf
	modified:   main_dc/yalarba/api_es/Dockerfile
	modified:   main_dc/yalarba/api_es/cmd/main.go
	modified:   main_dc/yalarba/api_es/go.mod
	new file:   main_dc/yalarba/api_es/go.sum
add api_es for REST API backend to easysite, add config nginx, add
server connection from api_es to db_tp
2025-10-29 06:11:21 +05:00
valitovgaziz 53477c6a34 deleted: main_dc/BB/api_bb/go_bbb.mod
new file:   main_dc/yalarba/api_es/.env
	new file:   main_dc/yalarba/api_es/Dockerfile
	new file:   main_dc/yalarba/api_es/cmd/main.go
	new file:   main_dc/yalarba/api_es/go.mod
	new file:   main_dc/yalarba/api_es/readme.md
	deleted:    main_dc/yalarba/api_tp/Makefile
create api_es with main.go
2025-10-29 05:54:04 +05:00
valitovgaziz 7b8e1384e2 modified: main_dc/yalarba/easySite/easySite/app/components/layout/Footer.vue
modified:   main_dc/yalarba/easySite/easySite/app/components/layout/Header.vue
	modified:   main_dc/yalarba/easySite/easySite/app/layouts/default.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/index.vue
add footer into main page for easysite
2025-10-29 04:56:26 +05:00
valitovgaziz e06ac476e6 modified: main_dc/yalarba/easySite/easySite/app/components/layout/Header.vue
modified:   main_dc/yalarba/easySite/easySite/app/composables/useObjects.ts
	modified:   main_dc/yalarba/easySite/easySite/app/pages/auth/login.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/auth/register.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/index.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/objects/[id]/edit.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/objects/create.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/objects/my-objects.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/profile/index.vue
	modified:   main_dc/yalarba/easySite/easySite/nuxt.config.ts
set main index page for easysite102.ru with header and search bar
2025-10-29 02:26:06 +05:00
valitovgaziz b3d7de5857 modified: main_dc/yalarba/easySite/easySite/app/components/forms/LoginForm.vue
modified:   main_dc/yalarba/easySite/easySite/app/components/forms/ObjectForm.vue
	modified:   main_dc/yalarba/easySite/easySite/app/components/layout/Header.vue
	modified:   main_dc/yalarba/easySite/easySite/app/components/objects/ObjectCard.vue
	new file:   main_dc/yalarba/easySite/easySite/app/composables/objects/TourCard.vue
	modified:   main_dc/yalarba/easySite/easySite/app/composables/useAuth.ts
	modified:   main_dc/yalarba/easySite/easySite/app/composables/useObjects.ts
	modified:   main_dc/yalarba/easySite/easySite/app/layouts/auth.vue
	modified:   main_dc/yalarba/easySite/easySite/app/layouts/default.vue
	modified:   main_dc/yalarba/easySite/easySite/app/middleware/auth.ts
	modified:   main_dc/yalarba/easySite/easySite/app/pages/auth/login.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/index.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/objects/[id]/edit.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/objects/[id]/index.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/objects/create.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/objects/index.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/objects/my-objects.vue
add pages for easysite102.ru and a lot of ather thinks
2025-10-29 01:51:31 +05:00
valitovgaziz 5c609a3641 modified: main_dc/yalarba/easySite/easySite/nuxt.config.ts
config for fonts
2025-10-29 00:56:08 +05:00
valitovgaziz bcfef0878f new file: main_dc/yalarba/easySite/.env
new file:   main_dc/yalarba/easySite/easySite/app/assets/css/components.css
	new file:   main_dc/yalarba/easySite/easySite/app/assets/css/fonts.css
	new file:   main_dc/yalarba/easySite/easySite/app/assets/css/travel-typography.css
	new file:   main_dc/yalarba/easySite/easySite/app/assets/css/travel.css
	new file:   main_dc/yalarba/easySite/easySite/app/assets/css/typography.css
	new file:   main_dc/yalarba/easySite/easySite/app/assets/css/variables.css
	modified:   main_dc/yalarba/easySite/easySite/nuxt.config.ts
add css font, styles and ather files for style site easySite
2025-10-29 00:53:57 +05:00
valitovgaziz 6f724d99a2 new file: main_dc/yalarba/easySite/easySite/app/assets/css/main.css
modified:   main_dc/yalarba/easySite/easySite/nuxt.config.ts
	new file:   package-lock.json
	new file:   package.json
add main styles for all project, fix warn with word process by
installing some npm install --save-dev @types/node
2025-10-28 22:53:21 +05:00
valitovgaziz 2128977ce0 modified: main_dc/yalarba/easySite/easySite/app/pages/index.vue
change main page for easysite
2025-10-28 22:46:12 +05:00
valitovgaziz 1ed840b5d4 deleted: main_dc/yalarba/easySite/easy-site/prod/index.html 2025-10-28 22:41:19 +05:00
valitovgaziz b9d2011d75 modified: README.md
working FrontEnd nuxt_es
2025-10-28 22:37:28 +05:00
valitovgaziz 1e187f4cad modified: main_dc/Makefile
add commands for easysite
2025-10-28 22:21:29 +05:00
valitovgaziz b4dadb0fb5 modified: main_dc/yalarba/easySite/easySite/Dockerfile
change start command for easysite from preview to node .../index.mjs
2025-10-28 22:18:03 +05:00
valitovgaziz 7b916c6cbe modified: main_dc/nginx/nginx-ssl.conf
change nginx ngix-ssl.conf for easysite102.ru
2025-10-28 22:15:32 +05:00
valitovgaziz 53c84a0d3d modified: main_dc/yalarba/easySite/easySite/Dockerfile
change npm run start to npm run preview
2025-10-28 22:05:00 +05:00
valitovgaziz 28691774e9 modified: main_dc/Makefile
add Makefile commands for build easysite
2025-10-28 21:54:50 +05:00
valitovgaziz 6e5ccb95b6 modified: main_dc/yalarba/easySite/easySite/Dockerfile
simplyfy the Dockerfile into easysite
2025-10-28 21:48:30 +05:00
valitovgaziz bd4cc23101 modified: main_dc/yalarba/easySite/easySite/Dockerfile
update dockerfile to thiner multistage build
2025-10-28 09:08:58 +05:00
valitovgaziz 32b0754395 modified: main_dc/yalarba/easySite/easySite/Dockerfile
change the Docerfile
2025-10-28 07:30:47 +05:00
valitovgaziz e7c5123f14 modified: main_dc/docker-compose.yml
new file:   main_dc/yalarba/easySite/easySite/.data/content/contents.sqlite
	modified:   main_dc/yalarba/easySite/easySite/.gitignore
	modified:   main_dc/yalarba/easySite/easySite/Dockerfile
change into Dockerfile for easySite node verstion from 18 to 20
2025-10-28 06:25:37 +05:00
valitovgaziz b999916073 modified: main_dc/docker-compose.yml
modified:   main_dc/nginx/nginx-ssl.conf
	new file:   main_dc/yalarba/easySite/easySite/.dockerignore
	new file:   main_dc/yalarba/easySite/easySite/Dockerfile
	modified:   main_dc/yalarba/easySite/easySite/app/layouts/admin.vue
	modified:   main_dc/yalarba/easySite/easySite/app/layouts/auth.vue
	modified:   main_dc/yalarba/easySite/easySite/app/layouts/default.vue
	modified:   main_dc/yalarba/easySite/easySite/nuxt.config.ts
	modified:   main_dc/yalarba/easySite/easySite/package-lock.json
	modified:   main_dc/yalarba/easySite/easySite/package.json
	deleted:    main_dc/yalarba/easySite/package-lock.json
build target, set new conteiner for easysite102.ru
2025-10-28 06:18:42 +05:00
valitovgaziz 9666d7d3a7 modified: main_dc/yalarba/easySite/easySite/app/layouts/default.vue
modified:   main_dc/yalarba/easySite/easySite/app/pages/admin/objects.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/admin/settings.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/admin/users.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/auth/login.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/auth/register.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/index.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/objects/[id]/edit.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/objects/[id]/index.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/objects/create.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/objects/index.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/objects/my-objects.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/objects/search.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/profile/edit.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/profile/index.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/profile/settings.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/reviews/index.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/reviews/write.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/support/[id].vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/support/create.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/support/index.vue
	modified:   main_dc/yalarba/easySite/easySite/nuxt.config.ts
	modified:   main_dc/yalarba/easySite/easySite/package-lock.json
	modified:   main_dc/yalarba/easySite/easySite/package.json
add project structure easySite
2025-10-28 02:53:21 +05:00
valitovgaziz 2a2475f13a add structure project 2025-10-28 02:24:42 +05:00
valitovgaziz 5b3c26d74e modified: main_dc/valitovgaziz/html/index.html
new file:   main_dc/yalarba/easySite/package-lock.json
	new file:   main_dc/yalarba/easySite/spa_es/.gitignore
	new file:   main_dc/yalarba/easySite/spa_es/README.md
	new file:   main_dc/yalarba/easySite/spa_es/app/app.vue
	new file:   main_dc/yalarba/easySite/spa_es/eslint.config.mjs
	new file:   main_dc/yalarba/easySite/spa_es/nuxt.config.ts
	new file:   main_dc/yalarba/easySite/spa_es/package-lock.json
	new file:   main_dc/yalarba/easySite/spa_es/package.json
	new file:   main_dc/yalarba/easySite/spa_es/public/favicon.ico
	new file:   main_dc/yalarba/easySite/spa_es/public/robots.txt
	new file:   main_dc/yalarba/easySite/spa_es/tsconfig.json
init nuxt app for fronend easySite
2025-10-27 11:32:31 +05:00
valitovgaziz 49a22832d6 modified: main_dc/valitovgaziz/html/index.html
modified:   main_dc/valitovgaziz/html/style.css
add section about search teammate for projects
2025-10-27 10:00:07 +05:00
valitovgaziz 07f6b8ff54 modified: main_dc/valitovgaziz/html/darkTheme.css
modified:   main_dc/valitovgaziz/html/scripts.js
	modified:   main_dc/valitovgaziz/html/style.css
add dark theme for repository card info
2025-10-27 09:45:16 +05:00
valitovgaziz c04cceb7f8 new file: main_dc/valitovgaziz/html/images/ValitovGaziz/valitovgaziz2.jpg
new file:   main_dc/valitovgaziz/html/images/ValitovGaziz/valitovgaziz3.jpg
	modified:   main_dc/valitovgaziz/html/index.html
	modified:   main_dc/valitovgaziz/html/style.css
add some photo into valitovgaziz site
2025-10-27 09:32:59 +05:00
valitovgaziz 661f27f39a modified: main_dc/valitovgaziz/html/darkTheme.css
modified:   main_dc/valitovgaziz/html/darkThemeToggle.js
	modified:   main_dc/valitovgaziz/html/index.html
add dark theme for valitovgaziz site
2025-10-26 12:37:12 +05:00
valitovgaziz 1c774ce80c modified: main_dc/BB/bbvue/src/components/EventCard.vue
modified:   main_dc/BB/bbvue/src/router/index.js
	modified:   main_dc/BB/bbvue/src/stores/event.js
	modified:   main_dc/BB/bbvue/src/views/Events.vue
	modified:   main_dc/BB/bbvue/src/views/Profile.vue
add events page and event store and eventcard modal
2025-10-25 05:50:36 +05:00
valitovgaziz 21487660d6 modified: main_dc/BB/bbvue/src/App.vue
new file:   main_dc/BB/bbvue/src/components/EventCard.vue
	new file:   main_dc/BB/bbvue/src/components/RegistrationCard.vue
	modified:   main_dc/BB/bbvue/src/router/index.js
	new file:   main_dc/BB/bbvue/src/stores/event.js
	modified:   main_dc/BB/bbvue/src/stores/helpers/api.js
	new file:   main_dc/BB/bbvue/src/stores/personalBests.js
	new file:   main_dc/BB/bbvue/src/stores/stats.js
	new file:   main_dc/BB/bbvue/src/views/DetailedStats.vue
	new file:   main_dc/BB/bbvue/src/views/Events.vue
	new file:   main_dc/BB/bbvue/src/views/PersonalBests.vue
	modified:   main_dc/BB/bbvue/src/views/Profile.vue
add pages for stats, best-personal and stores
2025-10-25 05:10:04 +05:00
valitovgaziz 001a3fb0a0 modified: main_dc/BB/bbvue/src/App.vue
modified:   main_dc/BB/bbvue/src/components/NavigationMenu.vue
delete incon and add into h-menu logic with login/logout state and menu
item Вход\выход
2025-10-25 03:02:24 +05:00
valitovgaziz ea807a00e9 modified: README.md
modified:   main_dc/BB/bbvue/src/components/NavigationMenu.vue
add logic for change Войти Выйти into h-menu
2025-10-25 01:35:43 +05:00
valitovgaziz 0bb5f13fc5 new file: main_dc/BB/bbvue/src/assets/fonts/fastrun-outline.otf
fix name path
2025-10-24 13:05:54 +05:00
valitovgaziz b828c6cfad modified: main_dc/BB/bbvue/src/assets/main.css
modified:   main_dc/BB/bbvue/src/views/Home.vue
fast run font-family on home page main text
2025-10-24 13:02:14 +05:00
valitovgaziz eea907fe23 modified: main_dc/BB/bbvue/src/App.vue
modified:   main_dc/BB/bbvue/src/components/NavigationMenu.vue
scale on hover r-link profile icon
2025-10-24 12:38:59 +05:00
valitovgaziz c6340d1b55 modified: main_dc/BB/bbvue/src/App.vue
header icon 1rem for padding gap within menu and profile icon
2025-10-24 12:04:16 +05:00
valitovgaziz 13204f1baf modified: main_dc/BB/bbvue/src/App.vue
modified:   main_dc/BB/bbvue/src/main.js
	modified:   main_dc/BB/bbvue/src/views/Profile.vue
add links from profile icons to login and logout on header
2025-10-24 11:44:05 +05:00
valitovgaziz 3ddf0f186c modified: main_dc/BB/bbvue/src/views/Profile.vue
add font-family for names into profile page begushiybashkir site
2025-10-24 11:26:35 +05:00
valitovgaziz ae5102eeed modified: main_dc/BB/bbvue/src/App.vue
fix bag with zero styles into h-menu
2025-10-24 11:15:33 +05:00
valitovgaziz badb7d2410 modified: main_dc/BB/bbvue/src/App.vue
add profile icon into header, near hamburger menu
2025-10-24 11:08:53 +05:00
valitovgaziz f54b6a06e3 modified: main_dc/BB/bbvue/package-lock.json
modified:   main_dc/BB/bbvue/package.json
	modified:   main_dc/BB/bbvue/src/App.vue
	modified:   main_dc/BB/bbvue/src/components/NavigationMenu.vue
install import { Icon } from '@iconify/vue'
2025-10-24 08:57:32 +05:00
valitovgaziz 4085816f00 modified: main_dc/docker-compose.yml
change volumes names
2025-10-24 05:33:02 +05:00
valitovgaziz 3d38ee1ef5 modified: main_dc/docker-compose.yml
fix path for .env into api_bb
2025-10-24 05:25:30 +05:00
valitovgaziz 15357fd3c0 create and moove into new directories for BegushiyBashkir and
yalarbacreate and moove into new directories for BegushiyBashkir and
yalarbacreate and moove into new directories for BegushiyBashkir and
yalarbacreate and moove into new directories for BegushiyBashkir and
yalarbacreate and moove into new directories for BegushiyBashkir and
yalarbacreate and moove into new directories for BegushiyBashkir and
yalarbacreate and moove into new directories for BegushiyBashkir and
yalarbacreate and moove into new directories for BegushiyBashkir and
yalarbacreate and moove into new directories for BegushiyBashkir and
yalarba
2025-10-24 05:22:44 +05:00
valitovgaziz 358c14428f modified: main_dc/docker-compose.yml
renamed:    valitovgaziz/.env -> main_dc/valitovgaziz/.env
	renamed:    valitovgaziz/html/assets/docs/TermSheet.pdf -> main_dc/valitovgaziz/html/assets/docs/TermSheet.pdf
	renamed:    valitovgaziz/html/darkTheme.css -> main_dc/valitovgaziz/html/darkTheme.css
	renamed:    valitovgaziz/html/darkThemeToggle.js -> main_dc/valitovgaziz/html/darkThemeToggle.js
	renamed:    valitovgaziz/html/images/ValitovGaziz/valitovgaziz.jpg -> main_dc/valitovgaziz/html/images/ValitovGaziz/valitovgaziz.jpg
	renamed:    valitovgaziz/html/images/favicon/code_orange.png -> main_dc/valitovgaziz/html/images/favicon/code_orange.png
	renamed:    valitovgaziz/html/images/favicon/icons8-vk-50.png -> main_dc/valitovgaziz/html/images/favicon/icons8-vk-50.png
	renamed:    "valitovgaziz/html/images/favicon/icons8-\321\202\320\265\320\273\320\265\320\263\321\200\320\260\320\274-50.png" -> "main_dc/valitovgaziz/html/images/favicon/icons8-\321\202\320\265\320\273\320\265\320\263\321\200\320\260\320\274-50.png"
	renamed:    valitovgaziz/html/index.html -> main_dc/valitovgaziz/html/index.html
	renamed:    valitovgaziz/html/saveContactsButtonStyle.css -> main_dc/valitovgaziz/html/saveContactsButtonStyle.css
	renamed:    valitovgaziz/html/scripts.js -> main_dc/valitovgaziz/html/scripts.js
	renamed:    valitovgaziz/html/style.css -> main_dc/valitovgaziz/html/style.css
	renamed:    valitovgaziz/html/style/about.css -> main_dc/valitovgaziz/html/style/about.css
	renamed:    valitovgaziz/html/style/footer.css -> main_dc/valitovgaziz/html/style/footer.css
	renamed:    valitovgaziz/html/style/hero_section.css -> main_dc/valitovgaziz/html/style/hero_section.css
	renamed:    valitovgaziz/html/style/social_link.css -> main_dc/valitovgaziz/html/style/social_link.css
	renamed:    valitovgaziz/html/style/yalarba_investmen.css -> main_dc/valitovgaziz/html/style/yalarba_investmen.css
	renamed:    yalarba/easySite/easy-site/prod/index.html -> main_dc/yalarba/easySite/easy-site/prod/index.html
	renamed:    yalarba/serv_spa/.env -> main_dc/yalarba/serv_spa/.env
	renamed:    yalarba/serv_spa/spa/Dockerfile -> main_dc/yalarba/serv_spa/spa/Dockerfile
	renamed:    yalarba/serv_spa/spa/app/assets/bage_logo.png -> main_dc/yalarba/serv_spa/spa/app/assets/bage_logo.png
	renamed:    yalarba/serv_spa/spa/app/index.html -> main_dc/yalarba/serv_spa/spa/app/index.html
	renamed:    yalarba/serv_spa/spa/app/styles/mainStyle.css -> main_dc/yalarba/serv_spa/spa/app/styles/mainStyle.css
	renamed:    yalarba/serv_spa/spa/vue/Makefile -> main_dc/yalarba/serv_spa/spa/vue/Makefile
	renamed:    yalarba/serv_spa/spa/vue/README.md -> main_dc/yalarba/serv_spa/spa/vue/README.md
	renamed:    yalarba/serv_spa/spa/vue/advices.txt -> main_dc/yalarba/serv_spa/spa/vue/advices.txt
	renamed:    yalarba/serv_spa/spa/vue/index.html -> main_dc/yalarba/serv_spa/spa/vue/index.html
	renamed:    yalarba/serv_spa/spa/vue/jsconfig.json -> main_dc/yalarba/serv_spa/spa/vue/jsconfig.json
	renamed:    yalarba/serv_spa/spa/vue/package-lock.json -> main_dc/yalarba/serv_spa/spa/vue/package-lock.json
	renamed:    yalarba/serv_spa/spa/vue/package.json -> main_dc/yalarba/serv_spa/spa/vue/package.json
	renamed:    yalarba/serv_spa/spa/vue/src/App.vue -> main_dc/yalarba/serv_spa/spa/vue/src/App.vue
	renamed:    yalarba/serv_spa/spa/vue/src/assets/colors.css -> main_dc/yalarba/serv_spa/spa/vue/src/assets/colors.css
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts.css -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts.css
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/OFL.txt -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/OFL.txt
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/OpenSans-Italic-VariableFont_wdth,wght.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/OpenSans-Italic-VariableFont_wdth,wght.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/OpenSans-Italic.woff -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/OpenSans-Italic.woff
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/OpenSans-Italic.woff2 -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/OpenSans-Italic.woff2
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/OpenSans-Regular.woff -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/OpenSans-Regular.woff
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/OpenSans-Regular.woff2 -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/OpenSans-Regular.woff2
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/OpenSans-VariableFont_wdth,wght.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/OpenSans-VariableFont_wdth,wght.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/README.txt -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/README.txt
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/hinted-OpenSans-Italic.eot -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/hinted-OpenSans-Italic.eot
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/hinted-OpenSans-Italic.svg -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/hinted-OpenSans-Italic.svg
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/hinted-OpenSans-Italic.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/hinted-OpenSans-Italic.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/hinted-OpenSans-Regular.eot -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/hinted-OpenSans-Regular.eot
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/hinted-OpenSans-Regular.svg -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/hinted-OpenSans-Regular.svg
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/hinted-OpenSans-Regular.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/hinted-OpenSans-Regular.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-Bold.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-Bold.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-BoldItalic.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-BoldItalic.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-ExtraBold.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-ExtraBold.ttf
renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-ExtraBoldItalic.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-ExtraBoldItalic.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-Italic.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-Italic.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-Light.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-Light.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-LightItalic.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-LightItalic.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-Medium.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-Medium.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-MediumItalic.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-MediumItalic.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-Regular.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-Regular.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-SemiBold.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-SemiBold.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-SemiBoldItalic.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-SemiBoldItalic.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-Bold.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-Bold.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-BoldItalic.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-BoldItalic.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-ExtraBold.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-ExtraBold.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-ExtraBoldItalic.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-ExtraBoldItalic.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-Italic.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-Italic.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-Light.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-Light.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-LightItalic.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-LightItalic.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-Medium.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-Medium.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-MediumItalic.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-MediumItalic.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-Regular.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-Regular.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-SemiBold.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-SemiBold.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-SemiBoldItalic.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-SemiBoldItalic.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-Bold.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-Bold.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-BoldItalic.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-BoldItalic.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-ExtraBold.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-ExtraBold.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-ExtraBoldItalic.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-ExtraBoldItalic.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-Italic.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-Italic.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-Light.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-Light.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-LightItalic.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-LightItalic.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-Medium.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-Medium.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-MediumItalic.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-MediumItalic.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-Regular.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-Regular.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-SemiBold.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-SemiBold.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-SemiBoldItalic.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-SemiBoldItalic.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/OFL.txt -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/OFL.txt
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/README.txt -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/README.txt
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/Roboto-Italic-VariableFont_wdth,wght.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/Roboto-Italic-VariableFont_wdth,wght.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/Roboto-Italic.woff -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/Roboto-Italic.woff
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/Roboto-Italic.woff2 -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/Roboto-Italic.woff2
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/Roboto-Regular.woff -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/Roboto-Regular.woff
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/Roboto-Regular.woff2 -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/Roboto-Regular.woff2
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/Roboto-VariableFont_wdth,wght.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/Roboto-VariableFont_wdth,wght.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/hinted-Roboto-Italic.eot -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/hinted-Roboto-Italic.eot
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/hinted-Roboto-Italic.svg -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/hinted-Roboto-Italic.svg
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/hinted-Roboto-Italic.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/hinted-Roboto-Italic.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/hinted-Roboto-Regular.eot -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/hinted-Roboto-Regular.eot
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/hinted-Roboto-Regular.svg -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/hinted-Roboto-Regular.svg
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/hinted-Roboto-Regular.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/hinted-Roboto-Regular.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto-Black.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto-Black.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto-BlackItalic.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto-BlackItalic.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto-Bold.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto-Bold.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto-BoldItalic.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto-BoldItalic.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto-ExtraBold.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto-ExtraBold.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto-ExtraBoldItalic.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto-ExtraBoldItalic.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto-ExtraLight.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto-ExtraLight.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto-ExtraLightItalic.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto-ExtraLightItalic.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto-Italic.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto-Italic.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto-Light.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto-Light.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto-LightItalic.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto-LightItalic.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto-Medium.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto-Medium.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto-MediumItalic.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto-MediumItalic.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto-Regular.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto-Regular.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto-SemiBold.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto-SemiBold.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto-SemiBoldItalic.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto-SemiBoldItalic.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto-Thin.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto-Thin.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto-ThinItalic.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto-ThinItalic.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-Black.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-Black.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-BlackItalic.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-BlackItalic.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-Bold.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-Bold.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-BoldItalic.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-BoldItalic.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-ExtraBold.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-ExtraBold.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-ExtraBoldItalic.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-ExtraBoldItalic.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-ExtraLight.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-ExtraLight.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-ExtraLightItalic.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-ExtraLightItalic.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-Italic.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-Italic.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-Light.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-Light.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-LightItalic.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-LightItalic.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-Medium.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-Medium.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-MediumItalic.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-MediumItalic.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-Regular.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-Regular.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-SemiBold.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-SemiBold.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-SemiBoldItalic.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-SemiBoldItalic.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-Thin.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-Thin.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-ThinItalic.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-ThinItalic.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-Black.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-Black.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-BlackItalic.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-BlackItalic.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-Bold.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-Bold.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-BoldItalic.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-BoldItalic.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-ExtraBold.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-ExtraBold.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-ExtraBoldItalic.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-ExtraBoldItalic.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-ExtraLight.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-ExtraLight.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-ExtraLightItalic.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-ExtraLightItalic.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-Italic.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-Italic.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-Light.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-Light.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-LightItalic.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-LightItalic.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-Medium.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-Medium.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-MediumItalic.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-MediumItalic.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-Regular.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-Regular.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-SemiBold.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-SemiBold.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-SemiBoldItalic.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-SemiBoldItalic.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-Thin.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-Thin.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-ThinItalic.ttf -> main_dc/yalarba/serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-ThinItalic.ttf
	renamed:    yalarba/serv_spa/spa/vue/src/assets/linksStyle.css -> main_dc/yalarba/serv_spa/spa/vue/src/assets/linksStyle.css
	renamed:    yalarba/serv_spa/spa/vue/src/assets/main.css -> main_dc/yalarba/serv_spa/spa/vue/src/assets/main.css
	renamed:    yalarba/serv_spa/spa/vue/src/auth/axios.js -> main_dc/yalarba/serv_spa/spa/vue/src/auth/axios.js
	renamed:    yalarba/serv_spa/spa/vue/src/auth/services/auth.service.js -> main_dc/yalarba/serv_spa/spa/vue/src/auth/services/auth.service.js
	renamed:    yalarba/serv_spa/spa/vue/src/auth/stores/auth.store.js -> main_dc/yalarba/serv_spa/spa/vue/src/auth/stores/auth.store.js
	renamed:    yalarba/serv_spa/spa/vue/src/auth/stores/store.js -> main_dc/yalarba/serv_spa/spa/vue/src/auth/stores/store.js
	renamed:    yalarba/serv_spa/spa/vue/src/auth/vueauth/auth.js -> main_dc/yalarba/serv_spa/spa/vue/src/auth/vueauth/auth.js
	renamed:    yalarba/serv_spa/spa/vue/src/auth/watch.js -> main_dc/yalarba/serv_spa/spa/vue/src/auth/watch.js
	renamed:    yalarba/serv_spa/spa/vue/src/components/about/about.vue -> main_dc/yalarba/serv_spa/spa/vue/src/components/about/about.vue
	renamed:    yalarba/serv_spa/spa/vue/src/components/about/commits.vue -> main_dc/yalarba/serv_spa/spa/vue/src/components/about/commits.vue
	renamed:    yalarba/serv_spa/spa/vue/src/components/about/developers.vue -> main_dc/yalarba/serv_spa/spa/vue/src/components/about/developers.vue
	renamed:    yalarba/serv_spa/spa/vue/src/components/about/filosofy.vue -> main_dc/yalarba/serv_spa/spa/vue/src/components/about/filosofy.vue
	renamed:    yalarba/serv_spa/spa/vue/src/components/feetback/feetback.vue -> main_dc/yalarba/serv_spa/spa/vue/src/components/feetback/feetback.vue
	renamed:    yalarba/serv_spa/spa/vue/src/components/footerB/footerB.vue -> main_dc/yalarba/serv_spa/spa/vue/src/components/footerB/footerB.vue
	renamed:    yalarba/serv_spa/spa/vue/src/components/header/darkThemeToggle.vue -> main_dc/yalarba/serv_spa/spa/vue/src/components/header/darkThemeToggle.vue
	renamed:    yalarba/serv_spa/spa/vue/src/components/header/fullHeader.vue -> main_dc/yalarba/serv_spa/spa/vue/src/components/header/fullHeader.vue
	renamed:    yalarba/serv_spa/spa/vue/src/components/header/headerMemu.vue -> main_dc/yalarba/serv_spa/spa/vue/src/components/header/headerMemu.vue
	renamed:    yalarba/serv_spa/spa/vue/src/components/header/logo-rl-about.vue -> main_dc/yalarba/serv_spa/spa/vue/src/components/header/logo-rl-about.vue
	renamed:    yalarba/serv_spa/spa/vue/src/components/header/toggleMenu.vue -> main_dc/yalarba/serv_spa/spa/vue/src/components/header/toggleMenu.vue
	renamed:    yalarba/serv_spa/spa/vue/src/components/images/YalArbaLogo300.png -> main_dc/yalarba/serv_spa/spa/vue/src/components/images/YalArbaLogo300.png
	renamed:    yalarba/serv_spa/spa/vue/src/components/images/icons/arrow_back_24dp_E8EAED_FILL0_wght400_GRAD0_opsz24.svg -> main_dc/yalarba/serv_spa/spa/vue/src/components/images/icons/arrow_back_24dp_E8EAED_FILL0_wght400_GRAD0_opsz24.svg
	renamed:    yalarba/serv_spa/spa/vue/src/components/images/icons/arrow_forward_24dp_E8EAED_FILL0_wght400_GRAD0_opsz24.svg -> main_dc/yalarba/serv_spa/spa/vue/src/components/images/icons/arrow_forward_24dp_E8EAED_FILL0_wght400_GRAD0_opsz24.svg
	renamed:    yalarba/serv_spa/spa/vue/src/components/images/icons/close_24dp_E8EAED_FILL0_wght400_GRAD0_opsz24.svg -> main_dc/yalarba/serv_spa/spa/vue/src/components/images/icons/close_24dp_E8EAED_FILL0_wght400_GRAD0_opsz24.svg
	renamed:    yalarba/serv_spa/spa/vue/src/components/images/icons/home_24dp_E8EAED_FILL0_wght400_GRAD0_opsz24.svg -> main_dc/yalarba/serv_spa/spa/vue/src/components/images/icons/home_24dp_E8EAED_FILL0_wght400_GRAD0_opsz24.svg
	renamed:    yalarba/serv_spa/spa/vue/src/components/images/icons/menu_24dp_E8EAED_FILL0_wght400_GRAD0_opsz24.svg -> main_dc/yalarba/serv_spa/spa/vue/src/components/images/icons/menu_24dp_E8EAED_FILL0_wght400_GRAD0_opsz24.svg
	renamed:    yalarba/serv_spa/spa/vue/src/components/images/logo150x150.png -> main_dc/yalarba/serv_spa/spa/vue/src/components/images/logo150x150.png
	renamed:    yalarba/serv_spa/spa/vue/src/components/images/photo_2025-01-25_05-57-24.jpg -> main_dc/yalarba/serv_spa/spa/vue/src/components/images/photo_2025-01-25_05-57-24.jpg
	renamed:    yalarba/serv_spa/spa/vue/src/components/images/restObject.jpg -> main_dc/yalarba/serv_spa/spa/vue/src/components/images/restObject.jpg
	renamed:    yalarba/serv_spa/spa/vue/src/components/inout/inout.vue -> main_dc/yalarba/serv_spa/spa/vue/src/components/inout/inout.vue
	renamed:    yalarba/serv_spa/spa/vue/src/components/inout/registration.vue -> main_dc/yalarba/serv_spa/spa/vue/src/components/inout/registration.vue
	renamed:    yalarba/serv_spa/spa/vue/src/components/profile/profile.vue -> main_dc/yalarba/serv_spa/spa/vue/src/components/profile/profile.vue
	renamed:    yalarba/serv_spa/spa/vue/src/components/profile/profileEdit.vue -> main_dc/yalarba/serv_spa/spa/vue/src/components/profile/profileEdit.vue
	renamed:    yalarba/serv_spa/spa/vue/src/components/restObject/restObject.vue -> main_dc/yalarba/serv_spa/spa/vue/src/components/restObject/restObject.vue
	renamed:    yalarba/serv_spa/spa/vue/src/components/restObject/restObjectEdit.vue -> main_dc/yalarba/serv_spa/spa/vue/src/components/restObject/restObjectEdit.vue
	renamed:    yalarba/serv_spa/spa/vue/src/components/restObject/restOjbectAdd.vue -> main_dc/yalarba/serv_spa/spa/vue/src/components/restObject/restOjbectAdd.vue
	renamed:    yalarba/serv_spa/spa/vue/src/components/saerch_results/results.vue -> main_dc/yalarba/serv_spa/spa/vue/src/components/saerch_results/results.vue
	renamed:    yalarba/serv_spa/spa/vue/src/components/searchLine/searchLine.vue -> main_dc/yalarba/serv_spa/spa/vue/src/components/searchLine/searchLine.vue
	renamed:    yalarba/serv_spa/spa/vue/src/components/settings.vue -> main_dc/yalarba/serv_spa/spa/vue/src/components/settings.vue
	renamed:    yalarba/serv_spa/spa/vue/src/components/support.vue -> main_dc/yalarba/serv_spa/spa/vue/src/components/support.vue
	renamed:    yalarba/serv_spa/spa/vue/src/locales/bak.json -> main_dc/yalarba/serv_spa/spa/vue/src/locales/bak.json
	renamed:    yalarba/serv_spa/spa/vue/src/locales/en.json -> main_dc/yalarba/serv_spa/spa/vue/src/locales/en.json
	renamed:    yalarba/serv_spa/spa/vue/src/locales/i18n.js -> main_dc/yalarba/serv_spa/spa/vue/src/locales/i18n.js
	renamed:    yalarba/serv_spa/spa/vue/src/locales/langToggle.vue -> main_dc/yalarba/serv_spa/spa/vue/src/locales/langToggle.vue
	renamed:    yalarba/serv_spa/spa/vue/src/locales/languages.json -> main_dc/yalarba/serv_spa/spa/vue/src/locales/languages.json
	renamed:    yalarba/serv_spa/spa/vue/src/locales/ru.json -> main_dc/yalarba/serv_spa/spa/vue/src/locales/ru.json
	renamed:    yalarba/serv_spa/spa/vue/src/locales/tat.json -> main_dc/yalarba/serv_spa/spa/vue/src/locales/tat.json
	renamed:    yalarba/serv_spa/spa/vue/src/main.js -> main_dc/yalarba/serv_spa/spa/vue/src/main.js
	renamed:    yalarba/serv_spa/spa/vue/src/router/index.js -> main_dc/yalarba/serv_spa/spa/vue/src/router/index.js
	renamed:    yalarba/serv_spa/spa/vue/src/views/AboutView.vue -> main_dc/yalarba/serv_spa/spa/vue/src/views/AboutView.vue
	renamed:    yalarba/serv_spa/spa/vue/src/views/FeetbackView.vue -> main_dc/yalarba/serv_spa/spa/vue/src/views/FeetbackView.vue
	renamed:    yalarba/serv_spa/spa/vue/src/views/FilosofyView.vue -> main_dc/yalarba/serv_spa/spa/vue/src/views/FilosofyView.vue
	renamed:    yalarba/serv_spa/spa/vue/src/views/HomeView.vue -> main_dc/yalarba/serv_spa/spa/vue/src/views/HomeView.vue
	renamed:    yalarba/serv_spa/spa/vue/src/views/LogInView.vue -> main_dc/yalarba/serv_spa/spa/vue/src/views/LogInView.vue
	renamed:    yalarba/serv_spa/spa/vue/src/views/ProfileView.vue -> main_dc/yalarba/serv_spa/spa/vue/src/views/ProfileView.vue
	renamed:    yalarba/serv_spa/spa/vue/src/views/RegistrationView.vue -> main_dc/yalarba/serv_spa/spa/vue/src/views/RegistrationView.vue
	renamed:    yalarba/serv_spa/spa/vue/src/views/RestObjectView.vue -> main_dc/yalarba/serv_spa/spa/vue/src/views/RestObjectView.vue
	renamed:    yalarba/serv_spa/spa/vue/src/views/ResultsView.vue -> main_dc/yalarba/serv_spa/spa/vue/src/views/ResultsView.vue
	renamed:    yalarba/serv_spa/spa/vue/src/views/SettingsView.vue -> main_dc/yalarba/serv_spa/spa/vue/src/views/SettingsView.vue
	renamed:    yalarba/serv_spa/spa/vue/src/views/SupportView.vue -> main_dc/yalarba/serv_spa/spa/vue/src/views/SupportView.vue
	renamed:    yalarba/serv_spa/spa/vue/tailwind.config.js -> main_dc/yalarba/serv_spa/spa/vue/tailwind.config.js
	renamed:    yalarba/serv_spa/spa/vue/vite.config.js -> main_dc/yalarba/serv_spa/spa/vue/vite.config.js
moove yalarba and valitovgaziz sites into main_dc
2025-10-24 05:12:57 +05:00
valitovgaziz e58f607169 modified: main_dc/docker-compose.yml
from long name to little api_tp name
2025-10-23 05:18:03 +05:00
valitovgaziz 0f0554df51 renamed: main_dc/bbvue/public/fonts/Audiowide-Regular.ttf -> main_dc/bbvue/src/assets/fonts/Audiowide-Regular.ttf
renamed:    main_dc/bbvue/public/fonts/FugazOne-Regular.ttf -> main_dc/bbvue/src/assets/fonts/FugazOne-Regular.ttf
	renamed:    main_dc/bbvue/public/fonts/Lobster-Regular.ttf -> main_dc/bbvue/src/assets/fonts/Lobster-Regular.ttf
	renamed:    main_dc/bbvue/public/fonts/Montserrat-VariableFont_wght.ttf -> main_dc/bbvue/src/assets/fonts/Montserrat-VariableFont_wght.ttf
	renamed:    main_dc/bbvue/public/fonts/NunitoSans-VariableFont_YTLC,opsz,wdth,wght.ttf -> main_dc/bbvue/src/assets/fonts/NunitoSans-VariableFont_YTLC,opsz,wdth,wght.ttf
	renamed:    main_dc/bbvue/public/fonts/Poppins-VariableFont_wght.otf -> main_dc/bbvue/src/assets/fonts/Poppins-VariableFont_wght.otf
	renamed:    main_dc/bbvue/public/fonts/Rubik-VariableFont_wght.ttf -> main_dc/bbvue/src/assets/fonts/Rubik-VariableFont_wght.ttf
	renamed:    main_dc/bbvue/public/fonts/VictorMono-VariableFont_wght.ttf -> main_dc/bbvue/src/assets/fonts/VictorMono-VariableFont_wght.ttf
	renamed:    main_dc/bbvue/public/fonts/fastrun-regular.otf -> main_dc/bbvue/src/assets/fonts/fastrun-regular.otf
	modified:   main_dc/bbvue/src/assets/main.css
fonts into src/assets and path @/src/assets/fonts/font.ttf
2025-10-23 05:12:38 +05:00
valitovgaziz 1f9e72da8b modified: main_dc/bbvue/src/assets/main.css
fonts into public and path is @/dist/assets/font.ttf
2025-10-23 05:10:14 +05:00
valitovgaziz e3d7df9fe6 modified: main_dc/bbvue/src/assets/main.css
fonts into public and path is ./assets/font/ttf
2025-10-23 05:04:38 +05:00
valitovgaziz c221912e8b modified: main_dc/bbvue/src/assets/main.css
fonts into public path is ./public/fonts/font.ttf
2025-10-23 05:01:54 +05:00
valitovgaziz 76d5518fc8 modified: main_dc/bbvue/src/assets/main.css
fonts into public path  is /public/fonts/font.ttf
2025-10-23 04:59:48 +05:00
valitovgaziz fb46994a38 modified: main_dc/bbvue/src/assets/main.css
fonts into public and path is /fonts/fonts.ttf
2025-10-23 04:58:08 +05:00
valitovgaziz a2f0ef134b modified: main_dc/bbvue/src/assets/main.css
delte assets from path
2025-10-23 04:54:08 +05:00
valitovgaziz 46dfa3dba1 renamed: main_dc/bbvue/src/assets/fonts/Audiowide-Regular.ttf -> main_dc/bbvue/public/fonts/Audiowide-Regular.ttf
renamed:    main_dc/bbvue/src/assets/fonts/Lobster-Regular.ttf -> main_dc/bbvue/public/fonts/Lobster-Regular.ttf
	renamed:    main_dc/bbvue/src/assets/fonts/Montserrat-VariableFont_wght.ttf -> main_dc/bbvue/public/fonts/Montserrat-VariableFont_wght.ttf
	renamed:    main_dc/bbvue/src/assets/fonts/NunitoSans-VariableFont_YTLC,opsz,wdth,wght.ttf -> main_dc/bbvue/public/fonts/NunitoSans-VariableFont_YTLC,opsz,wdth,wght.ttf
		renamed:    main_dc/bbvue/src/assets/fonts/Poppins-VariableFont_wght.otf -> main_dc/bbvue/public/fonts/Poppins-VariableFont_wght.otf
	enamed:    main_dc/bbvue/src/assets/fonts/Rubik-VariableFont_wght.ttf -> main_dc/bbvue/public/fonts/Rubik-VariableFont_wght.ttf
	renamed:    main_dc/bbvue/src/assets/fonts/VictorMono-VariableFont_wght.ttf -> main_dc/bbvue/public/fonts/VictorMono-VariableFont_wght.ttf
	renamed:    main_dc/bbvue/src/assets/fonts/fastrun-regular.otf -> main_dc/bbvue/public/fonts/fastrun-regular.otf
	modified:   main_dc/bbvue/src/assets/main.css
moove fonts to public and set paths to ./public/fonts/fonts.ttf
2025-10-23 04:52:24 +05:00
valitovgaziz dbbefca1ec modified: main_dc/bbvue/src/assets/main.css
modified:   main_dc/bbvue/vite.config.js
redo vitte config form ./src to ./
2025-10-23 04:48:20 +05:00
valitovgaziz 6d27a43595 renamed: main_dc/bbvue/public/fonts/Audiowide-Regular.ttf -> main_dc/bbvue/src/assets/fonts/Audiowide-Regular.ttf
renamed:    main_dc/bbvue/public/fonts/FugazOne-Regular.ttf -> main_dc/bbvue/src/assets/fonts/FugazOne-Regular.ttf
	renamed:    main_dc/bbvue/public/fonts/Lobster-Regular.ttf -> main_dc/bbvue/src/assets/fonts/Lobster-Regular.ttf
	renamed:    main_dc/bbvue/public/fonts/Montserrat-VariableFont_wght.ttf -> main_dc/bbvue/src/assets/fonts/Montserrat-VariableFont_wght.ttf
	renamed:    main_dc/bbvue/public/fonts/NunitoSans-VariableFont_YTLC,opsz,wdth,wght.ttf -> main_dc/bbvue/src/assets/fonts/NunitoSans-VariableFont_YTLC,opsz,wdth,wght.ttf
	renamed:    main_dc/bbvue/public/fonts/Poppins-VariableFont_wght.otf -> main_dc/bbvue/src/assets/fonts/Poppins-VariableFont_wght.otf
	renamed:    main_dc/bbvue/public/fonts/Rubik-VariableFont_wght.ttf -> main_dc/bbvue/src/assets/fonts/Rubik-VariableFont_wght.ttf
	renamed:    main_dc/bbvue/public/fonts/VictorMono-VariableFont_wght.ttf -> main_dc/bbvue/src/assets/fonts/VictorMono-VariableFont_wght.ttf
	renamed:    main_dc/bbvue/public/fonts/fastrun-regular.otf -> main_dc/bbvue/src/assets/fonts/fastrun-regular.otf
	modified:   main_dc/bbvue/src/assets/main.css
	modified:   main_dc/bbvue/vite.config.js
moove fonts form public to assets and set alias for ./src into vitte
config
2025-10-23 04:46:16 +05:00
valitovgaziz fba6887052 deleted: main_dc/bbvue/src/assets/fonts.css
modified:   main_dc/bbvue/src/assets/main.css
moove fonts from fonts.css to main.css
2025-10-23 04:42:21 +05:00
valitovgaziz 24c0278e9d deleted: main_dc/bbvue/src/assets/fonts/Audiowide-Regular.ttf
deleted:    main_dc/bbvue/src/assets/fonts/FugazOne-Regular.ttf
	deleted:    main_dc/bbvue/src/assets/fonts/Lobster-Regular.ttf
	deleted:    main_dc/bbvue/src/assets/fonts/Montserrat-VariableFont_wght.ttf
	deleted:    main_dc/bbvue/src/assets/fonts/NunitoSans-VariableFont_YTLC,opsz,wdth,wght.ttf
	deleted:    main_dc/bbvue/src/assets/fonts/Poppins-VariableFont_wght.otf
	deleted:    main_dc/bbvue/src/assets/fonts/Rubik-VariableFont_wght.ttf
	deleted:    main_dc/bbvue/src/assets/fonts/VictorMono-VariableFont_wght.ttf
	deleted:    main_dc/bbvue/src/assets/fonts/fastrun-regular.otf
delete fonts from assets
2025-10-23 04:36:56 +05:00
valitovgaziz c54c18dd30 modified: main_dc/bbvue/src/assets/fonts.css
fix lobster
2025-10-23 04:29:29 +05:00
valitovgaziz a7444e618b renamed: main_dc/bbvue/public/fontsC/Audiowide-Regular.ttf -> main_dc/bbvue/public/fonts/Audiowide-Regular.ttf
renamed:    main_dc/bbvue/public/fontsC/FugazOne-Regular.ttf -> main_dc/bbvue/public/fonts/FugazOne-Regular.ttf
	renamed:    main_dc/bbvue/public/fontsC/Lobster-Regular.ttf -> main_dc/bbvue/public/fonts/Lobster-Regular.ttf
	renamed:    main_dc/bbvue/public/fontsC/Montserrat-VariableFont_wght.ttf -> main_dc/bbvue/public/fonts/Montserrat-VariableFont_wght.ttf
	renamed:    main_dc/bbvue/public/fontsC/NunitoSans-VariableFont_YTLC,opsz,wdth,wght.ttf -> main_dc/bbvue/public/fonts/NunitoSans-VariableFont_YTLC,opsz,wdth,wght.ttf
	renamed:    main_dc/bbvue/public/fontsC/Poppins-VariableFont_wght.otf -> main_dc/bbvue/public/fonts/Poppins-VariableFont_wght.otf
	renamed:    main_dc/bbvue/public/fontsC/Rubik-VariableFont_wght.ttf -> main_dc/bbvue/public/fonts/Rubik-VariableFont_wght.ttf
	renamed:    main_dc/bbvue/public/fontsC/VictorMono-VariableFont_wght.ttf -> main_dc/bbvue/public/fonts/VictorMono-VariableFont_wght.ttf
	renamed:    main_dc/bbvue/public/fontsC/fastrun-regular.otf -> main_dc/bbvue/public/fonts/fastrun-regular.otf
	modified:   main_dc/bbvue/src/assets/fonts.css
public directory moove from fontsC to fonts
2025-10-23 04:26:50 +05:00
valitovgaziz 506b079a12 modified: main_dc/bbvue/src/assets/fonts.css
upclosed
2025-10-23 04:25:18 +05:00
valitovgaziz a4be77c197 modified: main_dc/bbvue/src/assets/fonts.css
url path to ./fonts/font
2025-10-23 04:22:59 +05:00
valitovgaziz 1d4452b0d9 modified: main_dc/bbvue/src/assets/fonts.css
change url for font to /
2025-10-23 04:17:12 +05:00
valitovgaziz 9d4ec06494 new file: main_dc/bbvue/public/fontsC/Audiowide-Regular.ttf
new file:   main_dc/bbvue/public/fontsC/FugazOne-Regular.ttf
	new file:   main_dc/bbvue/public/fontsC/Lobster-Regular.ttf
	new file:   main_dc/bbvue/public/fontsC/Montserrat-VariableFont_wght.ttf
	new file:   main_dc/bbvue/public/fontsC/NunitoSans-VariableFont_YTLC,opsz,wdth,wght.ttf
	new file:   main_dc/bbvue/public/fontsC/Poppins-VariableFont_wght.otf
	new file:   main_dc/bbvue/public/fontsC/Rubik-VariableFont_wght.ttf
	new file:   main_dc/bbvue/public/fontsC/VictorMono-VariableFont_wght.ttf
	new file:   main_dc/bbvue/public/fontsC/fastrun-regular.otf
	modified:   main_dc/bbvue/src/assets/fonts.css
change path for fons to public
2025-10-23 04:06:58 +05:00
valitovgaziz 5edd725cf4 modified: main_dc/bbvue/src/assets/fonts.css
fix paths for fonts throught @/assets/
2025-10-23 04:02:22 +05:00
valitovgaziz 30378da9bd modified: main_dc/bbvue/src/App.vue
deleted:    main_dc/bbvue/src/assets/base.css
	modified:   main_dc/bbvue/src/assets/fonts.css
	modified:   main_dc/bbvue/src/assets/fonts/Lobster-Regular.ttf
	modified:   main_dc/bbvue/src/assets/main.css
	modified:   main_dc/bbvue/src/components/writeLogo.vue
add to variables new added fonts
2025-10-23 03:48:01 +05:00
valitovgaziz d23c21b325 modified: main_dc/bbvue/src/assets/fonts.css
new file:   main_dc/bbvue/src/assets/fonts/Audiowide-Regular.ttf
	new file:   main_dc/bbvue/src/assets/fonts/NunitoSans-VariableFont_YTLC,opsz,wdth,wght.ttf
	new file:   main_dc/bbvue/src/assets/fonts/Poppins-VariableFont_wght.otf
	new file:   main_dc/bbvue/src/assets/fonts/Rubik-VariableFont_wght.ttf
	new file:   main_dc/bbvue/src/assets/fonts/fastrun-regular.otf
add new fonts into begushiybashkir site
2025-10-23 03:24:49 +05:00
valitovgaziz 8bd364b78d modified: main_dc/bbvue/src/views/Login.vue
autocomplite add
2025-10-23 03:00:38 +05:00
valitovgaziz 175808f94b deleted: package-lock.json
deleted:    package.json
delete
2025-10-23 02:58:16 +05:00
valitovgaziz fd7a55f626 rename long name to short name 2025-10-23 02:48:42 +05:00
valitovgaziz df18d2083d modified: serv_nginx/bbvue/src/main.js
modified:   serv_nginx/bbvue/src/router/index.js
	new file:   serv_nginx/bbvue/src/views/ForgotPassword.vue
	modified:   serv_nginx/bbvue/src/views/Home.vue
	modified:   serv_nginx/bbvue/src/views/Login.vue
	new file:   serv_nginx/bbvue/src/views/PasswordReset.vue
	modified:   serv_nginx/bbvue/src/views/Profile.vue
	modified:   serv_nginx/bbvue/src/views/ProfileEdit.vue
	new file:   serv_nginx/bbvue/src/views/VerifayEmail.vue
add email sender, verifay email and reset password on FrontEnd
2025-10-22 05:35:33 +05:00
valitovgaziz 1e678c4b7e modified: serv_nginx/api_bb/.env
modified:   serv_nginx/api_bb/go.mod
	modified:   serv_nginx/api_bb/go.sum
	modified:   serv_nginx/api_bb/internal/database/migrate.go
	modified:   serv_nginx/api_bb/internal/handlers/auth.go
	new file:   serv_nginx/api_bb/internal/handlers/email_handler.go
	modified:   serv_nginx/api_bb/internal/handlers/handlers.go
	modified:   serv_nginx/api_bb/internal/models/user.go
	new file:   serv_nginx/api_bb/internal/repository/email_repository.go
	modified:   serv_nginx/api_bb/internal/repository/user_repository.go
	modified:   serv_nginx/api_bb/internal/routes/routes.go
	new file:   serv_nginx/api_bb/internal/service/email_service.go
	modified:   serv_nginx/api_bb/internal/service/user_service.go
	new file:   serv_nginx/api_bb/pkg/email/email.go
add email sender, vrificator and reset password
2025-10-22 05:16:30 +05:00
valitovgaziz 64301b9135 modified: README.md
modified:   serv_nginx/api_bb/internal/config/config.go
	new file:   serv_nginx/api_bb/internal/models/email.go
add email config to config api_bb and email model into models
2025-10-22 01:30:16 +05:00
valitovgaziz e793552f99 modified: serv_nginx/bbvue/src/main.js
fix google metrix
2025-10-22 00:58:24 +05:00
valitovgaziz 393a9b5d36 modified: serv_nginx/bbvue/src/main.js
add yandex and google metrix
2025-10-22 00:28:25 +05:00
valitovgaziz 815034905c modified: serv_nginx/bbvue/package-lock.json
modified:   serv_nginx/bbvue/package.json
	modified:   serv_nginx/bbvue/src/main.js
add yandex and google search tags
2025-10-22 00:15:21 +05:00
valitovgaziz 86537329e1 modified: serv_nginx/nginx/nginx-ssl.conf
api api_bb delet backend tail
2025-10-21 23:22:31 +05:00
valitovgaziz dc2a6ec866 modified: serv_nginx/nginx/Dockerfile
revert nginx Dockerfile to ald version without keycloak settings
2025-10-21 23:10:15 +05:00
valitovgaziz ed0eb735ac modified: serv_nginx/Makefile
modified:   serv_nginx/docker-compose.yml
delete keycloak network
2025-10-21 22:39:15 +05:00
valitovgaziz 17e8b8169b modified: serv_nginx/docker-compose.yml
delete keycloak containers
2025-10-21 22:34:29 +05:00
valitovgaziz 1e8fcfea67 modified: serv_nginx/Makefile
add docker logs commad
2025-10-21 22:31:40 +05:00
valitovgaziz c04077bb8c modified: serv_nginx/docker-compose.yml
new file:   serv_nginx/keycloak/.env.keycloak
	new file:   serv_nginx/keycloak/realm-config/setup-realm.json
	modified:   serv_nginx/nginx/nginx-ssl.conf
revert to ald setting into nginx
2025-10-21 22:29:43 +05:00
valitovgaziz bb284c6293 modified: serv_nginx/keycloak/Dockerfile
Dockerfile with multipart build
2025-10-21 06:53:25 +05:00
valitovgaziz 9b1f5e0630 modified: serv_nginx/docker-compose.yml
set keycloak new
2025-10-21 06:50:56 +05:00
valitovgaziz 2e10a8cbaf modified: serv_nginx/docker-compose.yml
modified:   serv_nginx/keycloak/Dockerfile
	modified:   serv_nginx/keycloak/keycloak.conf
one more try keycloak set
2025-10-21 06:48:59 +05:00
valitovgaziz cea5880f94 modified: serv_nginx/Makefile
keycloak command full
2025-10-21 06:46:03 +05:00
valitovgaziz 5ffe30be2d modified: serv_nginx/keycloak/Dockerfile
modified:   serv_nginx/keycloak/keycloak.conf
change to alpina commands
2025-10-21 06:43:50 +05:00
valitovgaziz 3757699a52 modified: serv_nginx/Makefile
git pull command for keycloak
2025-10-21 06:38:48 +05:00
valitovgaziz b2351ed824 modified: serv_nginx/Makefile
make keycloak stop build start
2025-10-21 06:38:15 +05:00
valitovgaziz d60d657ce7 modified: serv_nginx/Makefile
modified:   serv_nginx/docker-compose.yml
	modified:   serv_nginx/keycloak/Dockerfile
	modified:   serv_nginx/keycloak/keycloak.conf
	modified:   serv_nginx/nginx/nginx-ssl.conf
change all for know
2025-10-21 06:37:17 +05:00
valitovgaziz 516780160e modified: serv_nginx/Makefile
restart command for keycloak
2025-10-21 06:27:53 +05:00
valitovgaziz de988a3d02 modified: serv_nginx/.env
modified:   serv_nginx/nginx/Dockerfile
	modified:   serv_nginx/nginx/nginx-ssl.conf
add new config new Dockerfile and nginx conf
2025-10-21 06:23:46 +05:00
valitovgaziz 257da2958c modified: serv_nginx/docker-compose.yml
set :
2025-10-21 06:17:12 +05:00
valitovgaziz 95b13dce07 modified: serv_nginx/docker-compose.yml 2025-10-21 06:16:19 +05:00
valitovgaziz 90a04cd065 modified: serv_nginx/docker-compose.yml 2025-10-21 06:13:55 +05:00
valitovgaziz 8dcdfca88a modified: serv_nginx/nginx/nginx-ssl.conf
to keycloak for auth path
2025-10-21 06:06:24 +05:00
valitovgaziz 0380900104 modified: serv_nginx/nginx/nginx-ssl.conf
delete redirect to auth
2025-10-21 06:01:55 +05:00
valitovgaziz 8584d52ebb modified: serv_nginx/docker-compose.yml
change keycloak service config into docker-compose.yaml file
2025-10-21 06:00:06 +05:00
valitovgaziz 74382de88d modified: serv_nginx/keycloak/keycloak.conf
modified:   serv_nginx/nginx/nginx-ssl.conf
change configs for keycloak to auth only domen path and change nginx
configs for this
2025-10-21 05:58:27 +05:00
valitovgaziz 72e7d8b5f2 modified: serv_nginx/docker-compose.yml
modified:   serv_nginx/keycloak/keycloak.conf
keycloak modifay configs and environment vars
2025-10-21 05:52:18 +05:00
valitovgaziz c633d0dbe0 modified: serv_nginx/keycloak/keycloak.conf
modified:   serv_nginx/nginx/nginx-ssl.conf
change ssl add admin into ngnx and set keycloak configs
2025-10-21 05:43:44 +05:00
valitovgaziz 8ba9ce6ae1 modified: serv_nginx/docker-compose.yml
new file:   serv_nginx/keycloak/Dockerfile
	modified:   serv_nginx/keycloak/keycloak.conf
fix keycloak
2025-10-21 05:24:48 +05:00
valitovgaziz a2c888a19d modified: serv_nginx/Makefile
git pull first
2025-10-21 04:49:59 +05:00
valitovgaziz 706b3d3130 modified: serv_nginx/docker-compose.yml
try throught .env file config
2025-10-21 04:48:31 +05:00
valitovgaziz ea0ea5e7ad modified: serv_nginx/docker-compose.yml
new file:   serv_nginx/keycloak/keycloak.conf
fix space for kk
2025-10-21 04:45:37 +05:00
valitovgaziz 5a3461f886 modified: serv_nginx/docker-compose.yml
add keycloak service change commands
2025-10-21 04:40:22 +05:00
valitovgaziz 18beebaa58 modified: serv_nginx/docker-compose.yml
fix keycloak service space
2025-10-21 04:35:22 +05:00
valitovgaziz 3415ab2d60 modified: serv_nginx/Makefile
add git into re_kk command
2025-10-21 04:32:27 +05:00
valitovgaziz 110e102c62 modified: serv_nginx/Makefile
add command re_kk
2025-10-21 04:31:59 +05:00
valitovgaziz ddee121f4a modified: serv_nginx/docker-compose.yml
change space for keycloak service
2025-10-21 04:31:11 +05:00
valitovgaziz ff08c3ff68 modified: serv_nginx/Makefile
add keycloak stop start commands
2025-10-21 04:24:28 +05:00
valitovgaziz 8dc89029f3 modified: serv_nginx/docker-compose.yml
change keycloak configs
2025-10-21 04:19:55 +05:00
valitovgaziz 5e37e8c920 modified: serv_nginx/Makefile
modified:   serv_nginx/docker-compose.yml
add some commands TO KECLOAK
2025-10-21 04:06:55 +05:00
valitovgaziz a5c5b986e0 modified: serv_nginx/Makefile
make stop build start re_all commands added
2025-10-21 03:52:24 +05:00
valitovgaziz 1d58396888 modified: serv_nginx/Makefile
deleted:    serv_nginx/keycloak/.env
add nginx stop build start into Makefile
2025-10-21 03:48:24 +05:00
valitovgaziz 3e832a774d modified: serv_nginx/.env
modified:   serv_nginx/bbvue/src/views/Members.vue
	modified:   serv_nginx/docker-compose.yml
	new file:   serv_nginx/keycloak/.env
	modified:   serv_nginx/nginx/nginx-ssl.conf
add keycloak and DB for keycloak and set nginx config for keycloak
2025-10-21 03:41:41 +05:00
valitovgaziz 78ca030dab modified: serv_nginx/bbvue/src/main.js
modified:   serv_nginx/bbvue/src/views/Login.vue
set WriteLogo ass global component
2025-10-20 22:25:36 +05:00
valitovgaziz d1d6361c91 modified: serv_nginx/bbvue/src/stores/user.js
add export useStore
2025-10-20 21:25:46 +05:00
valitovgaziz ca22f1cbcb modified: serv_nginx/bbvue/src/stores/auth.js
change import to user
2025-10-20 21:23:28 +05:00
valitovgaziz eadb9e33eb renamed: serv_nginx/bbvue/src/stores/user_store.js -> serv_nginx/bbvue/src/stores/user.js
modified:   serv_nginx/bbvue/src/views/Profile.vue
back to user.js
2025-10-20 21:21:49 +05:00
valitovgaziz 512275cd17 modified: serv_nginx/bbvue/src/views/Profile.vue
fix bag user_store.js naming
2025-10-20 21:19:50 +05:00
valitovgaziz b75c0b4f2b modified: serv_nginx/bbvue/src/stores/auth.js
renamed:    serv_nginx/bbvue/src/stores/user.js -> serv_nginx/bbvue/src/stores/user_store.js
	modified:   serv_nginx/bbvue/src/views/Members.vue
search-input width is corrected
2025-10-20 20:54:53 +05:00
valitovgaziz 79d79eb70d modified: serv_nginx/bbvue/src/stores/user.js
set training plan fetch from v1/user/training-plan GET
2025-10-20 06:43:29 +05:00
valitovgaziz 0087c94552 modified: serv_nginx/api_bb/internal/database/migrate.go
modified:   serv_nginx/bbvue/src/stores/user.js
add migration for trainingPlan struct
2025-10-20 06:38:25 +05:00
valitovgaziz b9f68b5dcb modified: serv_nginx/api_bb/internal/handlers/handlers.go
renamed:    "serv_nginx/api_bb/internal/handlers/training_plan_handler\321\216\320\277\321\211" -> serv_nginx/api_bb/internal/handlers/training_plan_handler.go
	modified:   serv_nginx/api_bb/internal/handlers/user_achievement_handler.go
	modified:   serv_nginx/api_bb/internal/routes/routes.go
add routing, handlers for trainingPlan object
2025-10-20 06:26:26 +05:00
valitovgaziz 5c9abf5f94 new file: "serv_nginx/api_bb/internal/handlers/training_plan_handler\321\216\320\277\321\211"
modified:   serv_nginx/api_bb/internal/models/training_plan.go
	new file:   serv_nginx/api_bb/internal/service/training_plan_service.go
create CRUD for traing_plan
2025-10-20 05:23:57 +05:00
valitovgaziz 7515a1314a modified: serv_nginx/bbvue/src/stores/user.js
modified:   serv_nginx/bbvue/src/views/Profile.vue
fix some bag
2025-10-20 03:54:55 +05:00
valitovgaziz 88046f722d modified: serv_nginx/bbvue/src/stores/user.js
modified:   serv_nginx/bbvue/src/views/Profile.vue
change GET path for personal-bests
2025-10-20 03:32:41 +05:00
valitovgaziz 402296b726 modified: serv_nginx/api_bb/internal/database/migrate.go
modified:   serv_nginx/api_bb/internal/handlers/handlers.go
	new file:   serv_nginx/api_bb/internal/handlers/personal_best_handler.go
	modified:   serv_nginx/api_bb/internal/models/personal_best.go
	modified:   serv_nginx/api_bb/internal/models/user_stats.go
	modified:   serv_nginx/api_bb/internal/repository/personal_best_repository.go
	modified:   serv_nginx/api_bb/internal/routes/routes.go
	modified:   serv_nginx/api_bb/internal/service/personal_best_service.go
	modified:   serv_nginx/bbvue/src/stores/user.js
personal bests add handler, rout, service, repository, logic and
migrations for
2025-10-20 03:06:06 +05:00
valitovgaziz 1d0d99e938 modified: serv_nginx/Makefile
pbl to vue_bb
2025-10-20 01:01:43 +05:00
valitovgaziz d1d6b24765 modified: serv_nginx/bbvue/src/views/Profile.vue
Fix bag with the not correct date on Profile page. Begushiybashkir.ru
site
2025-10-20 01:00:29 +05:00
valitovgaziz 9e2051cbfe modified: serv_nginx/api_bb/internal/service/user_service.go
implement UpdateProfile for userHeandler and for UserService.
2025-10-20 00:38:58 +05:00
valitovgaziz e3f77f3a84 modified: serv_nginx/bbvue/src/stores/auth.js
modified:   serv_nginx/bbvue/src/views/Profile.vue
	modified:   serv_nginx/bbvue/src/views/ProfileEdit.vue
save debug commit only
2025-10-19 21:43:09 +05:00
valitovgaziz 17b936f566 modified: serv_nginx/api_bb/internal/models/training_plan.go
modified:   serv_nginx/bbvue/src/views/Profile.vue
add shadow into avatar image profile page begushiybashkir.ru site
2025-10-19 20:43:36 +05:00
valitovgaziz 6f77fb08b3 modified: serv_nginx/api_bb/internal/service/personal_best_service.go
add update stats
2025-10-19 11:11:29 +05:00
valitovgaziz 6422d85727 modified: serv_nginx/api_bb/internal/database/migrate.go
new file:   serv_nginx/api_bb/internal/handlers/event_handler.go
	new file:   serv_nginx/api_bb/internal/handlers/event_registration_handler.go
	modified:   serv_nginx/api_bb/internal/handlers/handlers.go
	modified:   serv_nginx/api_bb/internal/models/event.go
	modified:   serv_nginx/api_bb/internal/routes/routes.go
	new file:   serv_nginx/api_bb/internal/service/event_registration_service.go
	new file:   serv_nginx/api_bb/internal/service/event_service.go
	new file:   serv_nginx/api_bb/pkg/middleware/admin_middleware.go
add admin middleware, add event and eventRegistration handlers, routes,
services, EndPoints
2025-10-19 10:54:45 +05:00
valitovgaziz a36b0aa933 modified: serv_nginx/api_bb/internal/handlers/auth.go
delete optional some function
2025-10-19 09:20:07 +05:00
valitovgaziz 42ead16848 modified: serv_nginx/api_bb/internal/database/migrate.go
modified:   serv_nginx/api_bb/internal/handlers/handlers.go
	new file:   serv_nginx/api_bb/internal/handlers/user_achievement_handler.go
	modified:   serv_nginx/api_bb/internal/routes/routes.go
	modified:   serv_nginx/api_bb/internal/service/achievement_service.go
	modified:   serv_nginx/api_bb/pkg/utils/validation.go
	modified:   serv_nginx/bbvue/src/views/Home.vue
add achievement's handler, routing, service, migrator gorm and update
repository
2025-10-19 09:17:03 +05:00
valitovgaziz a8de1799aa modified: serv_nginx/api_bb/internal/database/migrate.go
add migration for workout model
2025-10-19 08:24:09 +05:00
valitovgaziz 6c02f11570 modified: serv_nginx/api_bb/internal/handlers/handlers.go
new file:   serv_nginx/api_bb/internal/handlers/user_workout_handler.go
	modified:   serv_nginx/api_bb/internal/models/workout.go
	modified:   serv_nginx/api_bb/internal/repository/workout_repository.go
	modified:   serv_nginx/api_bb/internal/routes/routes.go
	new file:   serv_nginx/api_bb/internal/service/user_workout_service.go
	new file:   serv_nginx/api_bb/pkg/utils/response.go
	new file:   serv_nginx/api_bb/pkg/utils/validation.go
add workout EndPoints for workout struct, CRUD operations and logics
2025-10-19 08:20:21 +05:00
valitovgaziz 15c59b2f55 modified: serv_nginx/api_bb/internal/repository/user_stats_repository.go
new file:   serv_nginx/api_bb/internal/scripts/migrate_existing_users.go
	modified:   serv_nginx/api_bb/internal/service/auth_service.go
	modified:   serv_nginx/api_bb/internal/service/user_stats_service.go
	modified:   serv_nginx/bbvue/src/views/Achievements.vue
	modified:   serv_nginx/bbvue/src/views/Reviews.vue
	modified:   serv_nginx/bbvue/src/views/Training.vue
fix bag with no stats into table
2025-10-19 06:33:58 +05:00
valitovgaziz 693ece204d modified: serv_nginx/api_bb/internal/database/migrate.go
add migration for userStats.go struct
2025-10-19 06:04:19 +05:00
valitovgaziz 7377aeb89f modified: serv_nginx/Makefile
api_bb into round CI/CD
2025-10-19 05:58:10 +05:00
valitovgaziz c55310e2e0 modified: serv_nginx/api_bb/internal/handlers/handlers.go
new file:   serv_nginx/api_bb/internal/handlers/user_stats_handler.go
	modified:   serv_nginx/api_bb/internal/routes/routes.go
	modified:   serv_nginx/api_bb/internal/service/user_stats_service.go
add EndPoints for user stats
2025-10-19 05:56:09 +05:00
valitovgaziz f7db0a07eb modified: serv_nginx/Makefile
change Makefile, add npm run build to all(make)
2025-10-19 05:31:02 +05:00
valitovgaziz ed8f0943c3 modified: serv_nginx/api_bb/internal/handlers/auth.go
modified:   serv_nginx/api_bb/internal/handlers/avatar.go
	modified:   serv_nginx/api_bb/internal/handlers/health.go
	modified:   serv_nginx/api_bb/internal/handlers/user.go
	modified:   serv_nginx/api_bb/internal/routes/routes.go
	deleted:    serv_nginx/bbvue/src/stores/counter.js
	modified:   serv_nginx/bbvue/src/views/Profile.vue
update chi to last, moove the routing from handlers to router.go
2025-10-19 05:26:54 +05:00
valitovgaziz c358ba01c9 modified: serv_nginx/bbvue/src/components/writeLogo.vue
modified:   serv_nginx/bbvue/src/views/Home.vue
home page buttons background and scale for little displays fixs
2025-10-19 01:58:47 +05:00
valitovgaziz fc85d56720 modified: serv_nginx/bbvue/src/components/NavigationMenu.vue
add show into burger menu begushiybashkir.ru site
2025-10-19 01:22:22 +05:00
valitovgaziz b29396791f modified: serv_nginx/bbvue/src/App.vue
add shadow for write logo and image logo in the header
2025-10-19 00:12:56 +05:00
valitovgaziz 3ade3b5a97 add changed revileing header in ather pages begushiybashkir.ru site 2025-10-18 23:56:59 +05:00
valitovgaziz 53c3a8eba7 modified: serv_nginx/bbvue/src/App.vue
modified:   serv_nginx/bbvue/src/router/index.js
set notification
2025-10-18 06:41:40 +05:00
valitovgaziz 8adb466391 modified: serv_nginx/bbvue/src/router/index.js
modified:   serv_nginx/bbvue/src/views/Home.vue
change message and set button's new css rule
2025-10-18 06:27:05 +05:00
valitovgaziz 9d5e65abd4 modified: serv_nginx/bbvue/src/App.vue
revile header set timeout to 50
2025-10-18 06:01:35 +05:00
valitovgaziz cb6444067f new file: serv_nginx/bbvue/public/images/homePagePhoto/fullPhotoBB.jpg
modified:   serv_nginx/bbvue/src/App.vue
	modified:   serv_nginx/bbvue/src/assets/fonts.css
	new file:   serv_nginx/bbvue/src/assets/fonts/Montserrat-VariableFont_wght.ttf
	modified:   serv_nginx/bbvue/src/views/Home.vue
change maine page Home begushiybashkir.ru site
2025-10-18 05:55:42 +05:00
valitovgaziz 138deacf74 modified: serv_nginx/Makefile
add pbl
2025-10-18 04:04:49 +05:00
valitovgaziz 7ecf987c14 new file: serv_nginx/bbvue/public/images/homePagePhoto/JumpVoronkyRun.jpg
modified:   serv_nginx/bbvue/src/assets/base.css
	new file:   serv_nginx/bbvue/src/assets/fonts.css
	new file:   serv_nginx/bbvue/src/assets/fonts/FugazOne-Regular.ttf
	new file:   serv_nginx/bbvue/src/assets/fonts/VictorMono-VariableFont_wght.ttf
	modified:   serv_nginx/bbvue/src/assets/main.css
	modified:   serv_nginx/bbvue/src/views/Home.vue
add font and set it into home page h1 begushiybashkir.ru site
2025-10-18 04:00:57 +05:00
valitovgaziz 6f422e5867 modified: serv_nginx/bbvue/src/views/Home.vue
main photo linear-gradient is delete
2025-10-18 00:19:50 +05:00
valitovgaziz 24f2dbfeef new file: serv_nginx/bbvue/public/images/FastRun.jpg
new file:   serv_nginx/bbvue/public/images/VoronkyRunBB.jpg
	modified:   serv_nginx/bbvue/src/views/Home.vue
up new main phoro into Home page begushiybashkir.ru site
2025-10-18 00:06:10 +05:00
valitovgaziz 597af017a4 modified: serv_nginx/Makefile
modified:   serv_nginx/bbvue/src/views/Profile.vue
avatar Url
2025-10-17 11:17:49 +05:00
valitovgaziz 1a1e0cfe9d modified: serv_nginx/bbvue/src/views/Profile.vue
delte onAvatarUpload method for methods:
2025-10-17 11:08:30 +05:00
valitovgaziz 96963c5752 modified: serv_nginx/bbvue/src/views/Profile.vue
set user.avatar
2025-10-17 11:03:32 +05:00
valitovgaziz 47fc741c72 modified: serv_nginx/bbvue/src/views/Profile.vue
add logs
2025-10-17 10:57:57 +05:00
valitovgaziz e4882e68bd modified: serv_nginx/bbvue/src/views/Profile.vue
delete background image
2025-10-17 10:55:22 +05:00
valitovgaziz 87d0635b49 modified: serv_nginx/bbvue/src/views/Profile.vue
try fix with dowble img
2025-10-17 10:48:52 +05:00
valitovgaziz 27394b1a99 modified: serv_nginx/bbvue/src/views/Profile.vue
simplify avatar image
2025-10-17 10:39:06 +05:00
valitovgaziz 1f076c45dd modified: serv_nginx/bbvue/src/views/Profile.vue
try fix bag with dowble avatar
2025-10-17 10:31:45 +05:00
valitovgaziz 1234090ffd modified: serv_nginx/bbvue/src/views/Profile.vue
upload avatar delete from profile page
2025-10-17 10:12:29 +05:00
valitovgaziz 3f88a78144 modified: serv_nginx/bbvue/src/stores/user.js
modified:   serv_nginx/bbvue/src/views/Profile.vue
create new user store
2025-10-17 10:05:55 +05:00
valitovgaziz 6de3abbbaa modified: serv_nginx/bbvue/src/views/Profile.vue
modify Profile page
2025-10-17 09:40:03 +05:00
valitovgaziz 17dac03dac new file: serv_nginx/api_bb/internal/repository/workout_repository.go
add workout repository
2025-10-17 09:23:23 +05:00
valitovgaziz 38a160b944 new file: serv_nginx/api_bb/internal/repository/gallery_repository.go
new file:   serv_nginx/api_bb/internal/repository/training_plan_repository.go
add gallery and training_plan repository
2025-10-17 09:14:23 +05:00
valitovgaziz 7cb4a75a44 new file: package-lock.json
new file:   package.json
	modified:   serv_nginx/bbvue/src/views/Members.vue
	modified:   serv_nginx/bbvue/src/views/News.vue
fix bag with tailwind css rules,
2025-10-17 08:54:37 +05:00
valitovgaziz 6625edc68c new file: serv_nginx/api_bb/internal/repository/event_registration_repository.go
new file:   serv_nginx/api_bb/internal/repository/event_repository.go
add event repo and event registration repository
2025-10-17 08:41:38 +05:00
valitovgaziz 284534a097 modified: serv_nginx/api_bb/go.mod
modified:   serv_nginx/api_bb/go.sum
renew go mod tidy
2025-10-17 08:38:40 +05:00
valitovgaziz 910e6f5e49 new file: serv_nginx/api_bb/internal/repository/achievement_repository.go
modified:   serv_nginx/api_bb/internal/repository/personal_best_repository.go
	new file:   serv_nginx/api_bb/internal/service/achievement_service.go
	new file:   serv_nginx/api_bb/internal/service/personal_best_service.go
	new file:   serv_nginx/api_bb/internal/service/user_stats_service.go
add some services
2025-10-17 08:25:32 +05:00
valitovgaziz c5bf8583e4 modified: serv_nginx/api_bb/internal/models/user_stats.go
new file:   serv_nginx/api_bb/internal/repository/personal_best_repository.go
	new file:   serv_nginx/api_bb/internal/repository/user_stats_repository.go
	new file:   serv_nginx/api_bb/pkg/utils/formatTime.go
add stats, personal_best repositories
2025-10-17 06:25:06 +05:00
valitovgaziz b19ce8fdfe modified: README.md
modified:   serv_nginx/api_bb/go.mod
	modified:   serv_nginx/api_bb/go.sum
	new file:   serv_nginx/api_bb/internal/models/achievement.go
	new file:   serv_nginx/api_bb/internal/models/common.go
	new file:   serv_nginx/api_bb/internal/models/event.go
	new file:   serv_nginx/api_bb/internal/models/gallery.go
	modified:   serv_nginx/api_bb/internal/models/news.go
	new file:   serv_nginx/api_bb/internal/models/personal_best.go
	new file:   serv_nginx/api_bb/internal/models/training_plan.go
	modified:   serv_nginx/api_bb/internal/models/user.go
	new file:   serv_nginx/api_bb/internal/models/user_stats.go
	modified:   serv_nginx/api_bb/internal/models/workout.go
	modified:   serv_nginx/bbvue/src/components/NavigationMenu.vue
	new file:   serv_nginx/bbvue/src/components/writeLogo.vue
add satructs for begushiybashkir.ru site
2025-10-17 05:09:53 +05:00
valitovgaziz 280d9a0eb3 modified: README.md
full upgare for profile page begushiybashkir.ru
2025-10-17 03:06:08 +05:00
valitovgaziz c38596f80f modified: README.md
host name
2025-10-17 03:03:59 +05:00
valitovgaziz 550b45a498 modified: README.md 2025-10-17 03:03:13 +05:00
valitovgaziz 7590df39d0 modified: README.md
modified:   serv_nginx/bbvue/README.md
nameing
2025-10-17 03:02:09 +05:00
valitovgaziz a145986fe9 modified: serv_nginx/api_bb/internal/handlers/user.go
modified:   serv_nginx/api_bb/internal/repository/user_repository.go
	modified:   serv_nginx/api_bb/internal/service/user_service.go
	modified:   serv_nginx/bbvue/src/views/Members.vue
set new page for members frontend
add new rounter path getAllUsers into backend
2025-10-17 01:57:50 +05:00
valitovgaziz 509704e9ba modified: serv_nginx/bbvue/src/stores/auth.js
change paths for url avatar manipulation
2025-10-16 13:26:49 +05:00
valitovgaziz d9535ac053 modified: serv_nginx/api_bb/internal/handlers/avatar.go
modified:   serv_nginx/api_bb/internal/handlers/user.go
	modified:   serv_nginx/api_bb/internal/routes/routes.go
	modified:   serv_nginx/api_bb/internal/service/avatar_service.go
set all avatars manipulating into avatar.go and remove from user.go
2025-10-16 13:14:27 +05:00
valitovgaziz e1ccf83569 modified: serv_nginx/api_bb/internal/routes/routes.go
change router path rounting
2025-10-16 12:54:40 +05:00
valitovgaziz 1704ceb91b modified: serv_nginx/api_bb/internal/handlers/avatar.go
add loggs into all methods
2025-10-16 12:42:19 +05:00
valitovgaziz 4afc8c2cfd modified: serv_nginx/Makefile
fix path bag
2025-10-16 11:51:44 +05:00
valitovgaziz b72fac4e78 modified: serv_nginx/docker-compose.yml
change path to volume directory
2025-10-16 11:19:41 +05:00
valitovgaziz 4a7e0ba364 modified: serv_nginx/bbvue/src/views/News.vue
new News.vue page
2025-10-16 10:30:17 +05:00
valitovgaziz bf9336d35a modified: serv_nginx/api_bb/internal/handlers/news_handler.go
add full of logs into newsHandler
2025-10-16 10:15:03 +05:00
valitovgaziz 8145a74f5e modified: serv_nginx/Makefile
change pipeline CI/CD
2025-10-16 10:03:24 +05:00
valitovgaziz 96e372a699 modified: serv_nginx/api_bb/internal/handlers/news_handler.go
del fmt upused import
2025-10-16 10:01:19 +05:00
valitovgaziz 56949ae731 modified: serv_nginx/api_bb/internal/handlers/news_handler.go
log createNews method auth
2025-10-16 10:00:02 +05:00
valitovgaziz 0c638f72a2 modified: serv_nginx/Makefile
add git pull command into pipline
2025-10-16 09:53:31 +05:00
valitovgaziz 74c9747836 modified: serv_nginx/Makefile
build CI/CD pipline Makefile
2025-10-16 09:52:09 +05:00
valitovgaziz 5d549c2079 modified: serv_nginx/Makefile
modified:   serv_nginx/api_bb/internal/handlers/news_handler.go
2025-10-16 09:48:34 +05:00
valitovgaziz a085b9b8b7 modified: serv_nginx/api_bb/internal/handlers/news_handler.go
add start end to CreateNews method
2025-10-16 09:41:05 +05:00
valitovgaziz 4f123ed399 modified: serv_nginx/Makefile
bb CI/CD pipline
2025-10-16 09:30:16 +05:00
valitovgaziz 4ade62764d modified: serv_nginx/api_bb/pkg/middleware/auth.go
start end add on debug level logger
2025-10-16 09:26:41 +05:00
valitovgaziz 09fe0f242a modified: serv_nginx/api_bb/pkg/middleware/auth.go
add some logs on debug level
2025-10-16 09:22:57 +05:00
valitovgaziz b3f93cc27d modified: serv_nginx/bbvue/src/views/News.vue
add checkAuth in onMounted
2025-10-16 09:03:58 +05:00
valitovgaziz 1c34a5578f modified: serv_nginx/Makefile
bbb
2025-10-16 08:32:29 +05:00
valitovgaziz 13206a0b39 modified: serv_nginx/api_bb/internal/handlers/news_handler.go
set fmt logger for seen userID
2025-10-16 08:29:55 +05:00
valitovgaziz 8628b5d238 modified: serv_nginx/bbvue/src/views/News.vue
to apiClient form http
2025-10-16 07:01:19 +05:00
valitovgaziz f527e85e4c modified: serv_nginx/bbvue/src/views/News.vue
set new News.vue page
2025-10-16 06:49:32 +05:00
valitovgaziz 5069d4a8ea modified: serv_nginx/Makefile
modified:   serv_nginx/api_bb/internal/handlers/news_handler.go
	modified:   serv_nginx/api_bb/internal/routes/routes.go
fix Makefile bb_db
fix news and comments auth function
2025-10-16 05:48:40 +05:00
valitovgaziz c24d0089f5 modified: serv_nginx/Makefile
fix bag
2025-10-16 05:35:40 +05:00
valitovgaziz a8f1f38acc modified: serv_nginx/Makefile
add start api_bb logs
2025-10-16 05:34:30 +05:00
valitovgaziz d00cd42cb1 modified: serv_nginx/Makefile
modified:   serv_nginx/docker-compose.yml
add vue3.js bbvue project build into Makefile
2025-10-16 02:45:27 +05:00
valitovgaziz 4fc0b68bcc modified: serv_nginx/docker-compose.yml
change path for bbvue and stubSite
2025-10-16 02:11:51 +05:00
valitovgaziz a4073040a5 modified: serv_nginx/docker-compose.yml
from ./ to /
2025-10-16 02:08:18 +05:00
valitovgaziz 329fa3753f modified: serv_nginx/Makefile
one more fix for nginx in Makefile
2025-10-16 02:06:16 +05:00
valitovgaziz c496a32cab modified: serv_nginx/Makefile
fix nginx Makefile
2025-10-16 02:05:16 +05:00
valitovgaziz d16b1cd1e7 add nginx stop build and run to Makefile 2025-10-16 02:03:51 +05:00
valitovgaziz 6ab25eb073 moove bbvue 2025-10-16 01:57:19 +05:00
valitovgaziz 4b04034f18 modified: serv_nginx/Makefile
postgres
2025-10-16 01:29:06 +05:00
valitovgaziz f039a26831 modified: serv_nginx/Makefile
shorter
2025-10-16 01:27:38 +05:00
valitovgaziz fdee898dd3 modified: serv_nginx/Makefile
add tab
2025-10-16 01:26:06 +05:00
valitovgaziz cd9fbc6d97 modified: serv_nginx/Makefile
bb_db fix:wq
2025-10-16 01:24:50 +05:00
valitovgaziz 3fa858831d modified: serv_nginx/Makefile
fix naming bb_bb in Makefile
2025-10-16 01:17:58 +05:00
valitovgaziz d0162cd415 renamed: serv_nginx/api_bb/Makefile -> serv_nginx/Makefile
move Makefile and add bb_bb make
2025-10-16 01:16:19 +05:00
valitovgaziz 6892fa8d23 modified: serv_nginx/api_bb/go.mod
modified:   serv_nginx/api_bb/go.sum
	modified:   serv_nginx/api_bb/internal/handlers/review_handler.go
	modified:   serv_nginx/api_bb/pkg/middleware/auth.go
fix bag with not authrizen requwest from back
change key String UserIDKey to middleware.UserIDKey
2025-10-16 00:53:31 +05:00
valitovgaziz 6c9bff1c91 modified: serv_nginx/api_bb/Makefile
modified:   serv_nginx/api_bb/internal/repository/review_repository.go
change or fix repo getStats function or method
2025-10-15 05:12:37 +05:00
valitovgaziz a200099f79 modified: begushiybashkir/bbvue/src/views/Reviews.vue
modified:   serv_nginx/api_bb/internal/repository/review_repository.go
fix null pointer execption in getStats repo function=method
2025-10-15 04:57:23 +05:00
valitovgaziz f545f5d71b modified: begushiybashkir/bbvue/src/views/Reviews.vue
reviews fix
2025-10-15 04:27:58 +05:00
valitovgaziz 3e1efbea37 modified: begushiybashkir/bbvue/src/views/Reviews.vue
fix bags with button cta and when login state can't set reviews
2025-10-15 04:05:02 +05:00
valitovgaziz b030c8d12a modified: begushiybashkir/bbvue/src/stores/helpers/api.js
modified:   begushiybashkir/bbvue/src/views/Reviews.vue
api client fix bag with import export api
2025-10-15 03:33:42 +05:00
valitovgaziz 0be15f1eaa modified: begushiybashkir/bbvue/src/views/Reviews.vue
set import throught { api }
2025-10-15 03:24:57 +05:00
valitovgaziz 8f02ad8a83 modified: serv_nginx/api_bb/Makefile
modified:   serv_nginx/api_bb/internal/database/migrate.go
set auto migrate for api_bb reviews table
2025-10-15 03:13:40 +05:00
valitovgaziz c0c8c3392a modified: .gitignore
new file:   serv_nginx/api_bb/Makefile
	new file:   serv_nginx/serv_golang_rest_api/Makefile
	new file:   yalarba/serv_spa/spa/vue/Makefile
remove Makefile from .gitignore, set makes for api_bb
2025-10-15 02:58:21 +05:00
valitovgaziz 0024d03635 modified: serv_nginx/api_bb/internal/handlers/review_handler.go
fix warnings
2025-10-15 02:55:08 +05:00
valitovgaziz 6d8e179f90 modified: begushiybashkir/bbvue/src/views/Reviews.vue
modified:   serv_nginx/api_bb/internal/handlers/handlers.go
	new file:   serv_nginx/api_bb/internal/handlers/review_handler.go
	new file:   serv_nginx/api_bb/internal/models/review.go
	new file:   serv_nginx/api_bb/internal/repository/review_repository.go
	modified:   serv_nginx/api_bb/internal/routes/routes.go
	new file:   serv_nginx/api_bb/internal/service/review_service.go
set reviews router, handler, service, repository
2025-10-15 02:48:41 +05:00
valitovgaziz 2327cd2f34 modified: README.md
отзывы комменты добавилл что бы не потерять
2025-10-15 00:14:27 +05:00
valitovgaziz f5510d0b8c modified: begushiybashkir/bbvue/src/components/AvatarUpload.vue
full url path set to avatars
2025-10-14 23:56:14 +05:00
valitovgaziz 07e0178516 modified: begushiybashkir/bbvue/src/views/Profile.vue
set cool path for src img avatar but not laud becouse authorization
2025-10-14 23:37:28 +05:00
valitovgaziz fcccb34b86 modified: serv_nginx/api_bb/internal/service/avatar_service.go
back to save full path into back
2025-10-14 23:02:56 +05:00
valitovgaziz 532862c12b modified: begushiybashkir/bbvue/src/views/Profile.vue
modified:   serv_nginx/api_bb/internal/service/avatar_service.go
try save path
2025-10-14 22:58:18 +05:00
valitovgaziz c01ce8a18c modified: begushiybashkir/bbvue/src/views/Profile.vue
modified:   serv_nginx/api_bb/internal/handlers/user.go
	modified:   serv_nginx/api_bb/internal/routes/routes.go
change url path into back to avatars
2025-10-14 22:45:30 +05:00
valitovgaziz fe8616a032 change getUrlAvatar path 2025-10-14 22:22:31 +05:00
valitovgaziz a5548178da modified: serv_nginx/api_bb/internal/handlers/user.go
chang serveAvatar function
2025-10-14 12:47:37 +05:00
valitovgaziz bbf470617b modified: serv_nginx/api_bb/internal/handlers/avatar.go
modified:   serv_nginx/api_bb/internal/handlers/handlers.go
	modified:   serv_nginx/api_bb/internal/handlers/user.go
	modified:   serv_nginx/api_bb/internal/routes/routes.go
	modified:   serv_nginx/api_bb/internal/service/avatar_service.go
	modified:   serv_nginx/nginx/nginx-ssl.conf
try to serve file name throught path
2025-10-14 12:41:16 +05:00
valitovgaziz 46549f5d22 modified: serv_nginx/nginx/nginx-ssl.conf
change to 443 location for uploads
2025-10-14 09:44:29 +05:00
valitovgaziz 8d2b1f053f modified: serv_nginx/nginx/nginx-ssl.conf
add slash to alias
2025-10-14 09:36:15 +05:00
valitovgaziz d0a16ef308 modified: serv_nginx/nginx/nginx-ssl.conf
uploads paths changed
2025-10-14 09:32:51 +05:00
valitovgaziz cb23657cad modified: serv_nginx/nginx/nginx-ssl.conf
change path for upload location path alias uploads
2025-10-14 09:26:18 +05:00
valitovgaziz 9c29f40872 modified: serv_nginx/nginx/nginx-ssl.conf
update server location for 80
2025-10-13 05:27:06 +05:00
valitovgaziz 8e93a20c45 modified: serv_nginx/nginx/nginx-ssl.conf
add location into 80 server listen
2025-10-13 05:23:49 +05:00
valitovgaziz be866a3e66 modified: serv_nginx/api_bb/internal/routes/routes.go
change serve static files
2025-10-13 05:18:18 +05:00
valitovgaziz 82c644dc9c modified: serv_nginx/api_bb/internal/handlers/handler_util.go
modified:   serv_nginx/api_bb/internal/handlers/handlers.go
	modified:   serv_nginx/api_bb/internal/handlers/user.go
	modified:   serv_nginx/api_bb/internal/service/user_service.go
change responseData for getProfile endPoint api_bb
2025-10-13 04:55:37 +05:00
valitovgaziz 61e8415f8b modified: begushiybashkir/bbvue/src/components/AvatarUpload.vue
modified:   begushiybashkir/bbvue/src/stores/auth.js
	modified:   begushiybashkir/bbvue/src/views/Profile.vue
avatar fix
2025-10-13 04:43:38 +05:00
valitovgaziz 988c4d428d modified: begushiybashkir/bbvue/src/components/AvatarUpload.vue
modified:   begushiybashkir/bbvue/src/views/Profile.vue
	modified:   begushiybashkir/bbvue/src/views/ProfileEdit.vue
set avatare initialize
2025-10-13 04:34:23 +05:00
valitovgaziz c693e481c8 modified: begushiybashkir/bbvue/src/stores/auth.js
update updateAvatar function
2025-10-13 04:25:00 +05:00
valitovgaziz 91f11ca1fc modified: begushiybashkir/bbvue/src/stores/auth.js
fix path for delete avatar
2025-10-13 04:13:02 +05:00
valitovgaziz bea819f81a modified: begushiybashkir/bbvue/.env
modified:   serv_nginx/api_bb/pkg/middleware/cors.go
	modified:   serv_nginx/api_bb/pkg/middleware/middleware.go
change middleware
2025-10-13 04:05:04 +05:00
valitovgaziz af9dc82c58 modified: begushiybashkir/bbvue/src/components/AvatarUpload.vue
modified:   begushiybashkir/bbvue/src/stores/auth.js
	modified:   serv_nginx/api_bb/.env
	modified:   serv_nginx/api_bb/internal/config/config.go
	modified:   serv_nginx/api_bb/internal/models/user.go
	modified:   serv_nginx/api_bb/internal/routes/routes.go
	modified:   serv_nginx/nginx/nginx-ssl.conf
some for get photo from api_bb
2025-10-13 03:12:42 +05:00
valitovgaziz b5ebe138cf modified: serv_nginx/docker-compose.yml
add volume for upload data photo avatar
2025-10-13 02:02:21 +05:00
valitovgaziz 39df128e1e modified: serv_nginx/api_bb/internal/handlers/avatar.go
add to response ok message
2025-10-13 01:17:58 +05:00
valitovgaziz e13545c5f1 new file: begushiybashkir/bbvue/src/components/AvatarUpload.vue
modified:   begushiybashkir/bbvue/src/stores/auth.js
	modified:   begushiybashkir/bbvue/src/views/Profile.vue
	modified:   begushiybashkir/bbvue/src/views/ProfileEdit.vue
	modified:   serv_nginx/api_bb/go.mod
	modified:   serv_nginx/api_bb/go.sum
	modified:   serv_nginx/api_bb/internal/handlers/auth.go
	new file:   serv_nginx/api_bb/internal/handlers/avatar.go
	modified:   serv_nginx/api_bb/internal/handlers/news_handler.go
	modified:   serv_nginx/api_bb/internal/handlers/user.go
	modified:   serv_nginx/api_bb/internal/models/user.go
	modified:   serv_nginx/api_bb/internal/repository/user_repository.go
	modified:   serv_nginx/api_bb/internal/routes/routes.go
	modified:   serv_nginx/api_bb/internal/service/auth_service.go
	new file:   serv_nginx/api_bb/internal/service/avatar_service.go
	modified:   serv_nginx/api_bb/internal/service/news_service.go
	modified:   serv_nginx/api_bb/internal/service/user_service.go
	modified:   serv_nginx/api_bb/pkg/logger/interface.go
	new file:   serv_nginx/api_bb/pkg/logger/route_logger.go
add structure fix, page, path, routes, component, authStore
for upload, renew and delete avatar
2025-10-13 00:51:13 +05:00
valitovgaziz 6bb475acb2 modified: begushiybashkir/bbvue/src/router/index.js
modified:   begushiybashkir/bbvue/src/views/Login.vue
	new file:   begushiybashkir/bbvue/src/views/Logout.vue
	modified:   begushiybashkir/bbvue/src/views/News.vue
	modified:   begushiybashkir/bbvue/src/views/Profile.vue
	modified:   serv_nginx/api_bb/cmd/main.go
	modified:   serv_nginx/api_bb/go.mod
	modified:   serv_nginx/api_bb/go.sum
	new file:   serv_nginx/api_bb/internal/app/app.go
	new file:   serv_nginx/api_bb/internal/database/database.go
	new file:   serv_nginx/api_bb/internal/database/migrate.go
	new file:   serv_nginx/api_bb/internal/handlers/news_handler.go
	new file:   serv_nginx/api_bb/internal/models/news.go
	new file:   serv_nginx/api_bb/internal/repository/comment_repository.go
	new file:   serv_nginx/api_bb/internal/repository/news_repository.go
	modified:   serv_nginx/api_bb/internal/routes/routes.go
	new file:   serv_nginx/api_bb/internal/service/news_service.go
	modified:   serv_nginx/api_bb/pkg/utils/utils.go
save router paths to login  logout profile from upsunction commit
2025-10-12 21:38:50 +05:00
valitovgaziz 12f805f9e1 new file: begushiybashkir/bbvue/public/images/locations/1mayPark.webp
new file:   begushiybashkir/bbvue/public/images/locations/dinamo.jpg
	new file:   begushiybashkir/bbvue/public/images/locations/riverSide.jpeg
	modified:   begushiybashkir/bbvue/src/views/Training.vue
	modified:   serv_nginx/api_bb/internal/handlers/handler_util.go
	modified:   serv_nginx/api_bb/internal/handlers/user.go
	modified:   serv_nginx/api_bb/internal/routes/routes.go
	modified:   serv_nginx/api_bb/internal/service/auth_service.go
	modified:   serv_nginx/api_bb/internal/service/user_service.go
add photo location into trainings page
2025-10-12 16:55:55 +05:00
valitovgaziz ebe43e6617 modified: begushiybashkir/bbvue/src/views/Training.vue
set traing's for chernayga
2025-10-12 10:50:07 +05:00
valitovgaziz 389b3d8a19 modified: begushiybashkir/bbvue/src/views/ProfileEdit.vue
fix bag with not upate info into pinia store
2025-10-12 10:35:38 +05:00
valitovgaziz 38bee8e077 modified: internal/handlers/user.go
modified:   internal/models/user.go
	modified:   internal/repository/user_repository.go
	modified:   internal/service/auth_service.go
	new file:   internal/service/user_service.go

	modified:   ../../begushiybashkir/bbvue/src/views/ProfileEdit.vue
fix bag not editable profile
2025-10-12 10:16:52 +05:00
valitovgaziz 8447dbe882 modified: begushiybashkir/bbvue/src/views/Profile.vue
some fix?
2025-10-12 07:33:02 +05:00
valitovgaziz 237ee6742e modified: begushiybashkir/bbvue/src/main.js
modified:   begushiybashkir/bbvue/src/stores/auth.js
	new file:   begushiybashkir/bbvue/src/stores/helpers/api.js
	new file:   begushiybashkir/bbvue/src/stores/index.js
	new file:   begushiybashkir/bbvue/src/stores/plugins/persistence.js
	modified:   begushiybashkir/bbvue/src/stores/user.js
	modified:   serv_nginx/api_bb/internal/models/user.go
	new file:   serv_nginx/api_bb/internal/models/workout.go
fix save store, add helpers, add new models
2025-10-12 06:11:54 +05:00
valitovgaziz fd9be2199c modified: begushiybashkir/bbvue/src/stores/auth.js
modified:   begushiybashkir/bbvue/src/stores/user.js
	modified:   begushiybashkir/bbvue/src/views/Login.vue
	modified:   begushiybashkir/bbvue/src/views/Register.vue
	modified:   serv_nginx/api_bb/.env
	modified:   serv_nginx/api_bb/cmd/main.go
	modified:   serv_nginx/api_bb/internal/handlers/auth.go
	modified:   serv_nginx/api_bb/internal/handlers/handlers.go
	modified:   serv_nginx/api_bb/internal/models/user.go
	modified:   serv_nginx/api_bb/internal/routes/routes.go
	modified:   serv_nginx/api_bb/internal/service/auth_service.go
	modified:   serv_nginx/api_bb/pkg/logger/logger.go
delete hash pass from auth_service
it is fix the not loging becouse dowble hash password was
password hash is in middlewares

	modified:   begushiybashkir/bbvue/src/stores/auth.js
	modified:   begushiybashkir/bbvue/src/stores/user.js
	modified:   begushiybashkir/bbvue/src/views/Login.vue
	modified:   begushiybashkir/bbvue/src/views/Register.vue
	modified:   serv_nginx/api_bb/.env
	modified:   serv_nginx/api_bb/cmd/main.go
	modified:   serv_nginx/api_bb/internal/handlers/auth.go
	modified:   serv_nginx/api_bb/internal/handlers/handlers.go
	modified:   serv_nginx/api_bb/internal/models/user.go
	modified:   serv_nginx/api_bb/internal/routes/routes.go
	modified:   serv_nginx/api_bb/internal/service/auth_service.go
	modified:   serv_nginx/api_bb/pkg/logger/logger.go
delete hash password from auth_server
becouse is dowble hash was
second hash is beforeCreatehash in User struct
2025-10-12 05:09:55 +05:00
valitovgaziz ce433e6187 modified: serv_nginx/api_bb/internal/handlers/auth.go
change method login
2025-10-11 11:30:45 +05:00
valitovgaziz 556780fd28 modified: serv_nginx/api_bb/pkg/middleware/middleware.go
new file:   serv_nginx/api_bb/pkg/middleware/options.go
delete one middleware
2025-10-11 11:21:39 +05:00
valitovgaziz 3d921c01e0 modified: serv_nginx/api_bb/.env
debug mod set
2025-10-11 11:10:16 +05:00
valitovgaziz 2ccab0a17f add middleware for perflite requests and delete cors from user and auth
handlers
2025-10-11 11:07:43 +05:00
valitovgaziz 6850701b47 modified: serv_nginx/api_bb/internal/handlers/user.go
editProfile change method form put to post
2025-10-11 10:11:21 +05:00
valitovgaziz 1e725dddbc modified: serv_nginx/api_bb/internal/routes/routes.go
chnage rounter and mounts for fix health & check
2025-10-11 10:00:40 +05:00
valitovgaziz 68a88ad30c modified: serv_nginx/api_bb/internal/routes/routes.go
fix heach check
2025-10-11 09:52:51 +05:00
valitovgaziz d20b5876cb modified: begushiybashkir/bbvue/src/views/ProfileEdit.vue
change path into bb site
2025-10-11 09:34:01 +05:00
valitovgaziz 6ba5d57b68 modified: serv_nginx/api_bb/internal/routes/routes.go
change to authed router for user
2025-10-11 09:27:02 +05:00
valitovgaziz 3807d060a8 divide auth and users handlers 2025-10-11 09:21:30 +05:00
valitovgaziz 0a66e544d6 add logger, godotenv 2025-10-11 08:40:37 +05:00
valitovgaziz 664232db0f modified: serv_nginx/api_bb/internal/handlers/auth.go
add logger into updateProfile function
2025-10-11 06:25:50 +05:00
valitovgaziz c79a642503 modified: serv_nginx/api_bb/internal/handlers/auth.go
add header allows into UpdateProfile method (function)
2025-10-11 06:19:23 +05:00
valitovgaziz ec969648cb modified: serv_nginx/api_bb/internal/handlers/auth.go
add oprions rout path to auth.go
2025-10-11 05:57:57 +05:00
valitovgaziz a5ca98b549 update auth_service, add updateProfile method into interface
change main package to api_bb
2025-10-11 05:41:15 +05:00
valitovgaziz 62b0d4763d modified: begushiybashkir/bbvue/src/views/ProfileEdit.vue
modified:   serv_nginx/api_bb/internal/handlers/auth.go
change path to editProfile for updateProfile handler
2025-10-11 05:17:13 +05:00
valitovgaziz 7eaddd9f37 modified: serv_nginx/api_bb/internal/handlers/auth.go
modified:   serv_nginx/api_bb/internal/service/auth_service.go
add update user path to put:profile
2025-10-11 05:11:21 +05:00
valitovgaziz 20d4913f50 modified: begushiybashkir/bbvue/src/components/NavigationMenu.vue
modified:   begushiybashkir/bbvue/src/main.js
	modified:   begushiybashkir/bbvue/src/router/index.js
	modified:   begushiybashkir/bbvue/src/stores/auth.js
	modified:   begushiybashkir/bbvue/src/views/Profile.vue
add profile link into header menu fix bag with save state
2025-10-11 04:15:23 +05:00
valitovgaziz fa38b1d487 add privacy polacy and TermsOfCervice pages and dowcuments for downloads 2025-10-10 06:18:20 +05:00
valitovgaziz 3184445ebc modified: serv_nginx/api_bb/internal/handlers/auth.go
change into reqest last and first names into json
2025-10-10 05:32:46 +05:00
valitovgaziz fe28795b84 modified: serv_nginx/api_bb/internal/handlers/auth.go
change request json to snake case form first_name to firstName and from
last_name to lastName
2025-10-10 05:24:53 +05:00
valitovgaziz 4274a345a9 modified: serv_nginx/api_bb/internal/handlers/auth.go
add import bytes and io
2025-10-10 05:16:22 +05:00
valitovgaziz 30de2eafdf new file: begushiybashkir/bbvue/.env
modified:   begushiybashkir/bbvue/src/stores/auth.js
	modified:   serv_nginx/api_bb/internal/handlers/auth.go
fix auth api_bb for debug info
2025-10-10 05:14:00 +05:00
valitovgaziz 38c1e43ec2 modified: begushiybashkir/bbvue/src/stores/auth.js
modified:   begushiybashkir/bbvue/src/views/Register.vue
	modified:   serv_nginx/api_bb/internal/handlers/auth.go
	modified:   serv_nginx/api_bb/internal/models/user.go
	modified:   serv_nginx/api_bb/internal/service/auth_service.go
fix some register bags and set debag info loggers
2025-10-10 04:48:42 +05:00
valitovgaziz b7252c7900 modified: serv_nginx/api_bb/internal/handlers/auth.go
modified:   serv_nginx/api_bb/pkg/middleware/middleware.go
set Access-Controll-Allow-Origin Origin
Allow-Credentials true
2025-10-10 03:18:34 +05:00
valitovgaziz 0e067c7477 modified: begushiybashkir/bbvue/package-lock.json
modified:   begushiybashkir/bbvue/package.json
	modified:   begushiybashkir/bbvue/src/main.js
	modified:   begushiybashkir/bbvue/src/router/index.js
	new file:   begushiybashkir/bbvue/src/stores/auth.js
	new file:   begushiybashkir/bbvue/src/stores/user.js
	modified:   begushiybashkir/bbvue/src/views/Login.vue
	modified:   begushiybashkir/bbvue/src/views/Profile.vue
	new file:   begushiybashkir/bbvue/src/views/ProfileEdit.vue
	modified:   begushiybashkir/bbvue/src/views/Register.vue
	modified:   serv_nginx/api_bb/bin/bb_api
	modified:   serv_nginx/api_bb/internal/handlers/auth.go
add axios, pinia store for user, auth, editProfile page
2025-10-10 01:30:30 +05:00
valitovgaziz affaa2679e modified: begushiybashkir/bbvue/src/views/Profile.vue
add photo path and names into profile page
2025-10-09 20:29:39 +05:00
valitovgaziz 6656dfbc7c modified: begushiybashkir/bbvue/src/views/Profile.vue
add profile page for begushiybashkir site
2025-10-09 06:01:46 +05:00
valitovgaziz 700e404a06 add rest api for api_bb vue a lot of files 2025-10-09 05:59:40 +05:00
valitovgaziz 654c682b05 modified: begushiybashkir/bbvue/src/views/Home.vue
modified:   serv_nginx/api_bb/internal/handlers/health.go
fix bachground photo path
2025-10-08 05:37:08 +05:00
valitovgaziz 7ec95ab722 modified: internal/routes/routes.go
add to v1/check the check function
2025-10-08 04:26:16 +05:00
valitovgaziz d5e11e992a modified: internal/routes/routes.go
health rounter from "/" and v1 anather
2025-10-08 04:15:50 +05:00
valitovgaziz 8b0de935bc modified: internal/routes/routes.go
health rount and /v1
2025-10-08 04:13:24 +05:00
valitovgaziz dabec4a74b modified: internal/routes/routes.go
chage rount into api_bb delete dbouble api
2025-10-08 04:04:13 +05:00
valitovgaziz 75aac825dd modified: Dockerfile
modified:   go.mod
	modified:   go.sum
change docker file delete CGO=0
2025-10-08 03:41:42 +05:00
valitovgaziz 6dd5f64ddb modified: serv_nginx/api_bb/cmd/main.go
modified:   serv_nginx/api_bb/go.mod
	modified:   serv_nginx/api_bb/go.sum
	modified:   serv_nginx/api_bb/internal/config/config.go
	modified:   serv_nginx/api_bb/pkg/database/database.go
change database to postgresql form sqlite
2025-10-08 03:35:39 +05:00
valitovgaziz abe5551619 modified: serv_nginx/api_bb/Dockerfile
change name ot main.go form server into dockerfile api_bb
2025-10-08 03:29:57 +05:00
valitovgaziz 91a373113f modified: serv_nginx/api_bb/Dockerfile
change cgo_enabled=1 to and goos=linux
2025-10-08 03:27:32 +05:00
valitovgaziz 48d820da75 modified: serv_nginx/api_bb/Dockerfile
dockerfile is fixed
2025-10-08 02:40:07 +05:00
valitovgaziz 13a2db15c0 modified: serv_nginx/nginx/nginx-ssl.conf
fix the path to api_bb ito begushiybashkir.ru location
2025-10-08 02:37:49 +05:00
valitovgaziz 4c1d04b0ec modified: serv_nginx/nginx/nginx-ssl.conf
add new location for rest api begushiybashkir.ru
2025-10-08 02:35:19 +05:00
valitovgaziz b30591432a modified: serv_nginx/docker-compose.yml
chang path for health check pint
2025-10-08 02:31:09 +05:00
valitovgaziz d5c768234b new file: serv_nginx/api_bb/Dockerfile
modified:   serv_nginx/docker-compose.yml
change docker compose yaml file, and add Dockerfile for api_bb
2025-10-08 02:25:56 +05:00
valitovgaziz 5e77a36b6a new file: serv_nginx/api_bb/bin/bb_api
modified:   serv_nginx/api_bb/internal/routes/routes.go
	new file:   serv_nginx/api_bb/sqlite
add bb_api routing check endpoint and create Makefile
2025-10-08 01:15:53 +05:00
valitovgaziz 23d68e53ab add bb_api rest api for begushiybashkir 2025-10-07 05:23:40 +05:00
valitovgaziz de0c992db9 modified: begushiybashkir/bbvue/src/views/About.vue
change paht with method getImageUrl
2025-10-07 04:24:41 +05:00
valitovgaziz e2fe264657 modified: begushiybashkir/bbvue/src/views/About.vue
about add getUrl method
2025-10-07 04:16:22 +05:00
valitovgaziz 400a95786d modified: begushiybashkir/bbvue/src/views/Gallery.vue
modified:   begushiybashkir/bbvue/vite.config.js
set public path
2025-10-07 04:09:50 +05:00
valitovgaziz 5dd4597686 moove images to public 2025-10-07 03:40:53 +05:00
valitovgaziz f3ef6ef7ca modified: src/views/Gallery.vue
set base url
2025-10-07 03:29:55 +05:00
valitovgaziz 639b8bb5dd modified: src/views/Gallery.vue
change path
2025-10-07 03:20:57 +05:00
valitovgaziz 0d497f1402 modified: src/views/Gallery.vue
change path for images slider to ../.. for dist
2025-10-07 03:17:27 +05:00
valitovgaziz 5db6f3bc1d modified: src/views/Gallery.vue
add some slash into paths
2025-10-07 03:14:29 +05:00
valitovgaziz 2be5fc525a add ../as.... 2025-10-07 03:11:37 +05:00
valitovgaziz 391012f8e1 delte src into begushiybashkir directory 2025-10-07 03:05:56 +05:00
valitovgaziz 23afe1b7eb modified: begushiybashkir/bbvue/src/views/Gallery.vue
add console.log
2025-10-07 02:59:47 +05:00
valitovgaziz 6476eea1f2 change names workout to slider 2025-10-07 02:17:36 +05:00
valitovgaziz fc0af1adcf add training photo into begushiybashkir site 2025-10-07 01:24:30 +05:00
valitovgaziz 24b0e59e35 delete and modify 2025-10-06 04:48:45 +05:00
valitovgaziz bca9161416 yalarba.ru front set new auth.store and auth.service 2025-10-06 04:44:30 +05:00
valitovgaziz 9065f34365 set new path for proxy pass and location, with slash in the end of paths 2025-10-06 02:48:10 +05:00
valitovgaziz 85c7c9cfb7 modified: serv_nginx/serv_golang_rest_api/internal/server/server.go
:
2025-10-06 02:28:49 +05:00
valitovgaziz c31699c918 modified: serv_nginx/docker-compose.yml
revese conteiner name and app-network name
2025-10-06 02:19:34 +05:00
valitovgaziz 27bc4c47c3 modified: serv_nginx/docker-compose.yml
set rest api name as api
2025-10-06 02:06:43 +05:00
valitovgaziz c78fb72401 modified: serv_nginx/serv_golang_rest_api/internal/server/server.go
rest api set rountin with /api/v1
2025-10-06 01:57:20 +05:00
valitovgaziz 4208527e35 modified: serv_nginx/nginx/nginx-ssl.conf
delete resolver
2025-10-06 01:34:50 +05:00
valitovgaziz 9be4a06742 modified: serv_nginx/serv_golang_rest_api/internal/server/server.go
change rountin into api
2025-10-06 01:30:58 +05:00
valitovgaziz b1dd054f17 modified: serv_nginx/nginx/nginx-ssl.conf
set proxy_pass to api:8080
2025-10-06 01:20:57 +05:00
valitovgaziz 7a861540d3 modified: serv_nginx/nginx/nginx-ssl.conf
set path /api proxy_pass to 0.0.0.0:8888
2025-10-06 01:12:11 +05:00
valitovgaziz 54a209ac65 modified: serv_nginx/nginx/nginx-ssl.conf
set localhost:8888 to api proxy_pass
2025-10-06 01:08:58 +05:00
valitovgaziz 749abdd496 modified: serv_nginx/nginx/nginx-ssl.conf
change path and add resolver
2025-10-06 00:54:18 +05:00
valitovgaziz 21cdc3e81a modified: serv_nginx/nginx/nginx-ssl.conf
set /api/ slech for both side location for rest_api
2025-10-06 00:43:14 +05:00
valitovgaziz 6af7057251 modified: serv_nginx/nginx/nginx-ssl.conf
add api into proxy_pass path
2025-10-06 00:39:46 +05:00
valitovgaziz 644d4a1ae7 modified: serv_nginx/nginx/nginx-ssl.conf
change path location api proxy_pass without last slash
2025-10-06 00:32:50 +05:00
valitovgaziz 36b9c45f6c modified: serv_nginx/docker-compose.yml
change name golang_serv into depends_on for nginx
2025-10-06 00:28:32 +05:00
valitovgaziz 67e34a26b5 modified: serv_nginx/docker-compose.yml
fix bag with 3 retares
2025-10-06 00:27:13 +05:00
valitovgaziz 30aed452f6 modified: serv_nginx/docker-compose.yml
modified:   serv_nginx/nginx/nginx-ssl.conf
	deleted:    serv_nginx/serv_golang_rest_api/docker-compose.yaml
add / slash to api path proxy_pass
2025-10-06 00:23:47 +05:00
valitovgaziz f113f8a8c9 modified: serv_nginx/serv_golang_rest_api/pkg/database/postgres.go
change automigrate for postgres connection gorm function
NewPostgresConnection
2025-10-06 00:00:27 +05:00
valitovgaziz c73701cf5c merge services api and nginx 2025-10-05 23:27:19 +05:00
valitovgaziz fd329f120d modified: serv_nginx/docker-compose.yml
modified:   serv_nginx/nginx/nginx-ssl.conf
add restpia network into nginx and set proxy for api the
serv_golang_rest_api:8080
2025-10-05 07:44:26 +05:00
valitovgaziz 2342bc98f9 modified: serv_nginx/nginx/nginx-ssl.conf
[200~host.docker.internal
2025-10-05 07:37:32 +05:00
valitovgaziz 5d352e7276 modified: serv_nginx/nginx/nginx-ssl.conf
172.17.0.1:8888
2025-10-05 07:33:42 +05:00
valitovgaziz c638f27fcf modified: serv_nginx/nginx/nginx-ssl.conf
change port 8888 to 8080
2025-10-05 06:20:57 +05:00
valitovgaziz a0467ddf4c modified: serv_nginx/nginx/nginx-ssl.conf
fix with naming microservice
2025-10-05 06:16:58 +05:00
valitovgaziz 10adec5567 modified: serv_nginx/nginx/nginx-ssl.conf
fix invalid conf in proxy pass
2025-10-05 06:05:20 +05:00
valitovgaziz 805a1274fb modified: serv_nginx/nginx/nginx-ssl.conf
delete space  into path to  http://localhost:8888
2025-10-05 06:02:29 +05:00
valitovgaziz 83c3fa2dfa modified: serv_nginx/nginx/nginx-ssl.conf
delte / from /api/
2025-10-05 05:59:14 +05:00
valitovgaziz 18bb2fa447 modified: yalarba/serv_golang_rest_api/internal/server/server.go
fix routin path with /
2025-10-05 05:53:39 +05:00
valitovgaziz e1a9beba18 modified: serv_nginx/nginx/nginx-ssl.conf
from 0.0.0.0 to localhost
2025-10-05 05:44:55 +05:00
valitovgaziz ec33cf7f2e modified: serv_nginx/nginx/nginx-ssl.conf
modified:   yalarba/serv_golang_rest_api/internal/server/server.go
add healcheck points into rest api server on golang for yalarba.ru
2025-10-05 02:50:12 +05:00
valitovgaziz 28a1078680 modified: serv_nginx/nginx/nginx-ssl.conf
modified:   yalarba/serv_golang_rest_api/internal/server/server.go
add setting and change path for url
2025-10-05 02:20:49 +05:00
valitovgaziz c02badcafc modified: yalarba/serv_golang_rest_api/internal/handlers/oauth_yandex.go
change userinfo.DefaultEmail to userinfo.Email into
h.findeOrCreateOUuthUser function
2025-10-05 02:12:58 +05:00
valitovgaziz 477ea72b1e modified: yalarba/serv_golang_rest_api/internal/models/o_auth_provider.go
deleted:    yalarba/serv_golang_rest_api/main.exe
	modified:   yalarba/serv_golang_rest_api/pkg/database/postgres.go
check auth, login, register on golang rest api
2025-10-05 02:00:50 +05:00
valitovgaziz 65931f5b08 delete easySite react files 2025-10-04 23:24:55 +05:00
valitovgaziz d543e3053a modified: serv_nginx/docker-compose.yml
change to prod config for nginx
2025-10-04 23:16:25 +05:00
valitovgaziz 9c192d8104 modified: serv_nginx/docker-compose.yml
easy site path for nginx configs changed into docker-compose.yamlfile
2025-10-04 23:13:38 +05:00
valitovgaziz 26f8a2d0b5 delete ald api on golang, moove file on yalarba project into one
directories
2025-10-04 23:07:40 +05:00
valitovgaziz ade401699c new file: serv_golang_rest_api/.gitignore
add new .gitingore file into api service
2025-10-04 22:43:10 +05:00
valitovgaziz 587ccc508f modified: .gitignore
gitignore delete duplicate lines
2025-10-04 22:18:58 +05:00
valitovgaziz 2b0baf9c4f modified: valitovgaziz/html/index.html
modified:   valitovgaziz/html/style.css
	new file:   valitovgaziz/html/style/footer.css
	modified:   valitovgaziz/html/style/social_link.css
some style on valitovgaziz's site
2025-10-04 02:50:17 +05:00
valitovgaziz 7f6a687b05 modified: valitovgaziz/html/index.html
modified:   valitovgaziz/html/style.css
	modified:   valitovgaziz/html/style/hero_section.css
valitovgaziz site change bachground color hearo section in header
2025-10-03 21:40:03 +05:00
valitovgaziz 35138a296e new file: valitovgaziz/html/assets/docs/TermSheet.pdf
modified:   valitovgaziz/html/index.html
	modified:   valitovgaziz/html/scripts.js
	modified:   valitovgaziz/html/style.css
	deleted:    valitovgaziz/html/style/up_button.css
add termsheet downloader function,
2025-10-02 17:33:45 +05:00
valitovgaziz 6e3f60c56c modified: valitovgaziz/html/index.html
modified:   valitovgaziz/html/scripts.js
add telegrem bot message sender
2025-10-02 16:37:55 +05:00
valitovgaziz f4ae7ba51e modified: begushiybashkir/bbvue/src/App.vue
new file:   valitovgaziz/html/images/favicon/icons8-vk-50.png
	new file:   "valitovgaziz/html/images/favicon/icons8-\321\202\320\265\320\273\320\265\320\263\321\200\320\260\320\274-50.png"
	modified:   valitovgaziz/html/index.html
	modified:   valitovgaziz/html/scripts.js
	modified:   valitovgaziz/html/style.css
	new file:   valitovgaziz/html/style/hero_section.css
	modified:   valitovgaziz/html/style/social_link.css
	new file:   valitovgaziz/html/style/up_button.css
	new file:   valitovgaziz/html/style/yalarba_investmen.css
modifay and change valitovgaziz site
2025-10-02 15:24:52 +05:00
valitovgaziz 7697cb403f new file: begushiybashkir/bbvue/src/components/NavigationMenu.vue
add navigationMenu.vue component to begushiybashkir site
2025-10-02 13:53:02 +05:00
valitovgaziz 56a5a4ea50 modified: begushiybashkir/bbvue/src/views/Members.vue
modified:   begushiybashkir/bbvue/src/views/Reviews.vue
	modified:   begushiybashkir/bbvue/src/views/Training.vue
add Members page for begushiybashkir site
2025-10-02 13:31:09 +05:00
valitovgaziz 4598caaec4 modified: begushiybashkir/bbvue/src/views/Reviews.vue
Create reviews page for begushiybashkir site
2025-10-02 13:10:47 +05:00
valitovgaziz e9cc909c11 modified: begushiybashkir/bbvue/src/App.vue
main page begushiybashkir change font size for write logo
2025-10-02 12:35:46 +05:00
valitovgaziz 5de869b31b modified: begushiybashkir/bbvue/src/views/News.vue
add news page into begushiybashkir site
2025-10-02 12:28:02 +05:00
valitovgaziz 5c7fb69090 modified: begushiybashkir/bbvue/src/views/Training.vue
add trainin page to begushiybashkir site
2025-10-02 12:11:58 +05:00
valitovgaziz 93a95c78e8 modified: begushiybashkir/bbvue/package.json
version: 0.0.13
2025-10-02 07:45:49 +05:00
valitovgaziz 7425254775 new file: begushiybashkir/bbvue/src/assets/images/slider/slider1.jpg
new file:   begushiybashkir/bbvue/src/assets/images/slider/slider10.jpg
	new file:   begushiybashkir/bbvue/src/assets/images/slider/slider11.jpg
	new file:   begushiybashkir/bbvue/src/assets/images/slider/slider12.jpg
	new file:   begushiybashkir/bbvue/src/assets/images/slider/slider13.jpg
	new file:   begushiybashkir/bbvue/src/assets/images/slider/slider14.jpg
	new file:   begushiybashkir/bbvue/src/assets/images/slider/slider15.jpg
	new file:   begushiybashkir/bbvue/src/assets/images/slider/slider16.jpg
	new file:   begushiybashkir/bbvue/src/assets/images/slider/slider17.jpg
	new file:   begushiybashkir/bbvue/src/assets/images/slider/slider18.jpg
	new file:   begushiybashkir/bbvue/src/assets/images/slider/slider19.jpg
	new file:   begushiybashkir/bbvue/src/assets/images/slider/slider2.jpg
	new file:   begushiybashkir/bbvue/src/assets/images/slider/slider20.jpg
	new file:   begushiybashkir/bbvue/src/assets/images/slider/slider21.jpg
	new file:   begushiybashkir/bbvue/src/assets/images/slider/slider22.jpg
	new file:   begushiybashkir/bbvue/src/assets/images/slider/slider23.png
	new file:   begushiybashkir/bbvue/src/assets/images/slider/slider24.jpg
	new file:   begushiybashkir/bbvue/src/assets/images/slider/slider4.jpg
	new file:   begushiybashkir/bbvue/src/assets/images/slider/slider5.jpg
	new file:   begushiybashkir/bbvue/src/assets/images/slider/slider6.jpg
	new file:   begushiybashkir/bbvue/src/assets/images/slider/slider7.jpg
	new file:   begushiybashkir/bbvue/src/assets/images/slider/slider8.jpg
	new file:   begushiybashkir/bbvue/src/assets/images/slider/slider9.jpg
	modified:   begushiybashkir/bbvue/src/views/Gallery.vue
add photo into begushiybashkir site, change page photos
2025-10-02 07:44:43 +05:00
valitovgaziz 7f53a4706a modified: begushiybashkir/bbvue/src/views/Achievements.vue
fix problem with naming view Achivement
2025-10-02 04:50:56 +05:00
valitovgaziz 785dbc0310 modified: begushiybashkir/bbvue/package.json
version 0.0.12
2025-10-02 04:49:35 +05:00
valitovgaziz ecdb09b019 modified: begushiybashkir/bbvue/src/views/Achievements.vue
Add achievements page to begushiybashkir site
2025-10-02 04:47:36 +05:00
valitovgaziz e414e16011 modified: README.md
plan to work on begushiybashkir site
2025-10-02 04:18:56 +05:00
valitovgaziz 1c5c17c6c4 modified: src/App.vue
change hamgurger menu into begushiybashkir site
2025-10-02 03:47:12 +05:00
valitovgaziz 56733294e4 modified: src/views/Home.vue
add phone click to action call and telegram message link
2025-10-02 02:16:45 +05:00
valitovgaziz c1d7779621 modified: index.html
add tite, tags and key words into index.html file begushiybashkir site
2025-10-02 02:04:34 +05:00
valitovgaziz 3a4cbc0ff1 modified: index.html
modified:   package.json
add logo favicon into begushiybashkir site
2025-10-02 02:01:36 +05:00
valitovgaziz e1af26ae76 modified: src/views/Home.vue
Change Home page to UI/UX usefull  theam
2025-10-02 01:56:31 +05:00
valitovgaziz e61734f448 modified: begushiybashkir/bbvue/src/App.vue
modified:   begushiybashkir/bbvue/src/views/About.vue
opacute for header and link for write log header to home page
2025-10-02 01:26:48 +05:00
valitovgaziz c4830ffe86 modified: begushiybashkir/bbvue/src/App.vue
modified:   begushiybashkir/bbvue/src/assets/base.css
	new file:   begushiybashkir/bbvue/src/assets/fonts/Lobster-Regular.ttf
add font family for write logo into begushiybashkir site
2025-10-02 01:07:27 +05:00
valitovgaziz f3387841ed modified: begushiybashkir/bbvue/src/views/About.vue
fix bag
2025-10-01 05:40:54 +05:00
valitovgaziz 995c49b783 modified: begushiybashkir/bbvue/src/App.vue
modified:   serv_golang_rest_api/internal/server/server.go
	new file:   serv_golang_rest_api/main
	new file:   serv_golang_rest_api/main.exe
change menu into main site begushiybashkir
2025-10-01 05:31:30 +05:00
valitovgaziz 4b0c07ddcb modified: serv_nginx/nginx/switch-config.sh
delete auth.yalarba.ru domen from switch-config.sh for nginx
2025-09-30 18:19:58 +05:00
valitovgaziz bfce43adce modified: serv_nginx/nginx/nginx-ssl.conf
delete auth.yalarba.ru form nginx config
2025-09-30 18:17:42 +05:00
valitovgaziz 89d2701479 modified: serv_nginx/docker-compose.yml
change dist of bbclub
2025-09-30 18:05:36 +05:00
valitovgaziz 802ea42961 modified: begushiybashkir/bbvue/src/views/About.vue
deleted:    begushiybashkir/bbvue/src/views/AboutView.vue
fix bag with naming img
2025-09-30 18:00:19 +05:00
valitovgaziz c437f5882f modified: begushiybashkir/bbvue/src/assets/base.css
modified:   begushiybashkir/bbvue/src/views/About.vue
add some css to base.css
2025-09-30 17:56:13 +05:00
valitovgaziz d19555d007 modified: begushiybashkir/bbvue/src/views/About.vue
fix bag
2025-09-30 16:26:34 +05:00
valitovgaziz 16585931ec modified: begushiybashkir/bbvue/src/App.vue
renamed:    "begushiybashkir/bbvue/src/assets/logo/\320\233\320\276\320\263\320\276\320\235\320\260\320\277\320\270\321\201\320\260\321\202\320\265\320\273\321\214\320\275\321\213\320\271.png" -> begushiybashkir/bbvue/src/assets/logo/WritedLogo.png
add write logo to header
2025-09-30 16:24:19 +05:00
valitovgaziz 73d165b279 deleted: begushiybashkir/bbvue/public/favicon.ico
modified:   begushiybashkir/bbvue/src/App.vue
	new file:   begushiybashkir/bbvue/src/assets/images/UMM2025.png
	new file:   begushiybashkir/bbvue/src/assets/images/UfaWhiteRiver.jpg
	new file:   begushiybashkir/bbvue/src/assets/images/dinamo.jpg
	new file:   begushiybashkir/bbvue/src/assets/images/i.webp
	new file:   begushiybashkir/bbvue/src/assets/logo/Logo.png
	new file:   begushiybashkir/bbvue/src/assets/logo/LogoNew.png
	new file:   "begushiybashkir/bbvue/src/assets/logo/\320\233\320\276\320\263\320\276\320\235\320\260\320\277\320\270\321\201\320\260\321\202\320\265\320\273\321\214\320\275\321\213\320\271.png"
	modified:   begushiybashkir/bbvue/src/views/About.vue
add images and log to begushiy bashkir site
2025-09-30 16:07:03 +05:00
valitovgaziz 2fc16f5551 modified: begushiybashkir/bbvue/src/views/About.vue
deleted:    begushiybashkir/bbvue/src/views/HomeView.vue
	modified:   begushiybashkir/bbvue/src/views/Register.vue
add about page into BegushiyBashkir site on vue3
2025-09-30 14:59:51 +05:00
valitovgaziz 019e462ef0 modified: begushiybashkir/bbvue/src/App.vue
new file:   begushiybashkir/bbvue/src/assets/images/Roshim2025_3.png
	new file:   begushiybashkir/bbvue/src/assets/images/ZagirTrainer3.jpg
	modified:   begushiybashkir/bbvue/src/assets/main.css
	modified:   begushiybashkir/bbvue/src/views/About.vue
	modified:   begushiybashkir/bbvue/src/views/Home.vue
	deleted:    begushiybashkir/vue/bashrunclub/.editorconfig
	deleted:    begushiybashkir/vue/bashrunclub/.gitattributes
	deleted:    begushiybashkir/vue/bashrunclub/.gitignore
	deleted:    begushiybashkir/vue/bashrunclub/.prettierrc.json
	deleted:    begushiybashkir/vue/bashrunclub/.vscode/extensions.json
	deleted:    begushiybashkir/vue/bashrunclub/README.md
	deleted:    begushiybashkir/vue/bashrunclub/eslint.config.js
	deleted:    begushiybashkir/vue/bashrunclub/index.html
	deleted:    begushiybashkir/vue/bashrunclub/jsconfig.json
	deleted:    begushiybashkir/vue/bashrunclub/package.json
	deleted:    begushiybashkir/vue/bashrunclub/public/favicon.ico
	deleted:    begushiybashkir/vue/bashrunclub/src/App.vue
	deleted:    begushiybashkir/vue/bashrunclub/src/components/common/Footer.vue
	deleted:    begushiybashkir/vue/bashrunclub/src/components/common/Header.vue
	deleted:    begushiybashkir/vue/bashrunclub/src/components/common/Navigation.vue
	deleted:    begushiybashkir/vue/bashrunclub/src/main.js
	deleted:    begushiybashkir/vue/bashrunclub/src/router/index.js
	deleted:    begushiybashkir/vue/bashrunclub/src/stores/auth.js
	deleted:    begushiybashkir/vue/bashrunclub/src/stores/counter.js
	deleted:    begushiybashkir/vue/bashrunclub/src/views/About.vue
	deleted:    begushiybashkir/vue/bashrunclub/src/views/Achivements.vue
	deleted:    begushiybashkir/vue/bashrunclub/src/views/Gallery.vue
	deleted:    begushiybashkir/vue/bashrunclub/src/views/Home.vue
	deleted:    begushiybashkir/vue/bashrunclub/src/views/Login.vue
	deleted:    begushiybashkir/vue/bashrunclub/src/views/Members.vue
	deleted:    begushiybashkir/vue/bashrunclub/src/views/News.vue
	deleted:    begushiybashkir/vue/bashrunclub/src/views/Profile.vue
	deleted:    begushiybashkir/vue/bashrunclub/src/views/Register.vue
	deleted:    begushiybashkir/vue/bashrunclub/src/views/Reviews.vue
	deleted:    begushiybashkir/vue/bashrunclub/src/views/Training.vue
	deleted:    begushiybashkir/vue/bashrunclub/vite.config.js
	deleted:    begushiybashkir/vue/package-lock.json
	deleted:    begushiybashkir/vue/package.json
restart project on vue
2025-09-30 14:46:46 +05:00
valitovgaziz e3f07504f2 start new bb run club project on vue3 2025-09-30 06:25:55 +05:00
valitovgaziz db66e5279b new file: begushiybashkir/vue/bashrunclub/.editorconfig
new file:   begushiybashkir/vue/bashrunclub/.gitattributes
	new file:   begushiybashkir/vue/bashrunclub/.gitignore
	new file:   begushiybashkir/vue/bashrunclub/.prettierrc.json
	new file:   begushiybashkir/vue/bashrunclub/.vscode/extensions.json
	new file:   begushiybashkir/vue/bashrunclub/README.md
	new file:   begushiybashkir/vue/bashrunclub/eslint.config.js
	new file:   begushiybashkir/vue/bashrunclub/index.html
	new file:   begushiybashkir/vue/bashrunclub/jsconfig.json
	new file:   begushiybashkir/vue/bashrunclub/package.json
	new file:   begushiybashkir/vue/bashrunclub/public/favicon.ico
	new file:   begushiybashkir/vue/bashrunclub/src/App.vue
	new file:   begushiybashkir/vue/bashrunclub/src/components/common/Footer.vue
	new file:   begushiybashkir/vue/bashrunclub/src/components/common/Header.vue
	new file:   begushiybashkir/vue/bashrunclub/src/components/common/Navigation.vue
	new file:   begushiybashkir/vue/bashrunclub/src/main.js
	new file:   begushiybashkir/vue/bashrunclub/src/router/index.js
	new file:   begushiybashkir/vue/bashrunclub/src/stores/auth.js
	new file:   begushiybashkir/vue/bashrunclub/src/stores/counter.js
	new file:   begushiybashkir/vue/bashrunclub/src/views/About.vue
	new file:   begushiybashkir/vue/bashrunclub/src/views/Achivements.vue
	new file:   begushiybashkir/vue/bashrunclub/src/views/Gallery.vue
	new file:   begushiybashkir/vue/bashrunclub/src/views/Home.vue
	new file:   begushiybashkir/vue/bashrunclub/src/views/Login.vue
	new file:   begushiybashkir/vue/bashrunclub/src/views/Members.vue
	new file:   begushiybashkir/vue/bashrunclub/src/views/News.vue
	new file:   begushiybashkir/vue/bashrunclub/src/views/Profile.vue
	new file:   begushiybashkir/vue/bashrunclub/src/views/Register.vue
	new file:   begushiybashkir/vue/bashrunclub/src/views/Reviews.vue
	new file:   begushiybashkir/vue/bashrunclub/src/views/Training.vue
	new file:   begushiybashkir/vue/bashrunclub/vite.config.js
	new file:   begushiybashkir/vue/package-lock.json
	new file:   begushiybashkir/vue/package.json
start new BegushiyBashkir club project on vue3
2025-09-30 04:28:55 +05:00
valitovgaziz 018f80098e modified: internal/config/oauth.go
modified:   internal/handlers/oauth.go
	new file:   internal/handlers/oauth_VK.go
	new file:   internal/handlers/oauth_yandex.go
	modified:   internal/utils/oauth_utils.go
add oauth for VK and ynadex and google
2025-09-30 03:19:33 +05:00
valitovgaziz c987e3ec19 modified: internal/server/server.go
new file:   internal/utils/oauth_utils.go
add utils oauth state generate
2025-09-30 00:32:14 +05:00
valitovgaziz f7b09e260c modified: go.mod
modified:   go.sum
	modified:   internal/handlers/auth.go
	new file:   internal/handlers/oauth.go
	modified:   internal/handlers/user_handler.go
	renamed:    internal/model/o_auth_provider.go -> internal/models/o_auth_provider.go
	renamed:    internal/model/user.go -> internal/models/user.go
	modified:   internal/repository/user_repository.go
	modified:   internal/service/user_service.go
	modified:   pkg/database/postgres.go
add  oauth_handler
2025-09-29 22:01:04 +05:00
valitovgaziz ca37a475c9 new file: internal/middleware/auth.go
add middleware for authenticate
2025-09-29 01:37:05 +05:00
valitovgaziz 8419b76452 renamed: internal/handler/auth.go -> internal/handlers/auth.go
renamed:    internal/handler/middleware.go -> internal/handlers/middleware.go
	renamed:    internal/handler/user_handler.go -> internal/handlers/user_handler.go
	modified:   internal/server/server.go
change handler to handlers name
2025-09-29 01:33:20 +05:00
valitovgaziz bf296373bd modified: go.mod
modified:   go.sum
	new file:   internal/config/oauth.go
	new file:   internal/handler/auth.go
	new file:   internal/utils/errors.go
	new file:   internal/utils/json.go
	modified:   internal/utils/password.go
Add utils, add auth handler, auth configs
2025-09-29 01:28:22 +05:00
valitovgaziz 83559faa4d new file: internal/utils/password.go
Add password utils hashPass and CheckPass functions
2025-09-29 00:58:34 +05:00
valitovgaziz 6adc52aa27 modified: internal/model/user.go
new file:   internal/utils/jwt.go
Add jwt utils
2025-09-29 00:57:03 +05:00
valitovgaziz b2bdb61733 modified: go.mod
modified:   go.sum
	new file:   internal/model/o_auth_provider.go
	modified:   internal/server/server.go
	deleted:    main
delete bin main in root directory
add OAuth struct
2025-09-29 00:39:04 +05:00
valitovgaziz ecbc2dbf69 modified: pkg/database/postgres.go
name AutoMigrate to autoMigrate
2025-09-27 06:01:08 +05:00
valitovgaziz a24cac773b modified: pkg/database/postgres.go
some last commmit
2025-09-27 05:59:49 +05:00
valitovgaziz 61d9e26c49 modified: pkg/database/postgres.go
fix autoMigrate
2025-09-27 05:48:51 +05:00
valitovgaziz 4c8b43dad7 modified: internal/service/user_service.go
fix bag with checking existens of user
2025-09-27 05:37:05 +05:00
valitovgaziz fdea48b959 modified: internal/service/user_service.go
fix bag add one more check for exits user when creat user
2025-09-27 05:13:58 +05:00
valitovgaziz 9706380a7d modified: internal/service/user_service.go
fic bag with check the user exits
2025-09-27 05:11:40 +05:00
valitovgaziz a2133fe72f modified: internal/service/user_service.go
fix check for
2025-09-27 05:10:34 +05:00
valitovgaziz 727c94d0a9 new file: .env
modified:   Dockerfile
	new file:   cmd/api/main.go
	modified:   docker-compose.yaml
	modified:   go.mod
	modified:   go.sum
	new file:   internal/config/config.go
	new file:   internal/handler/middleware.go
	new file:   internal/handler/user_handler.go
	new file:   internal/model/user.go
	new file:   internal/repository/user_repository.go
	new file:   internal/server/server.go
	new file:   internal/service/user_service.go
	new file:   main
	deleted:    main.go
	new file:   migrations/001_create_users.sql
	new file:   pkg/database/postgres.go
	deleted:    src/models/user.go
add files from deepseek for rest api server on golang gorm and chi
server router
2025-09-27 04:44:46 +05:00
valitovgaziz 12c855cacc modified: Dockerfile
new file:   src/models/user.go
add structure for serv_golang_rest_api service
2025-09-27 01:38:50 +05:00
valitovgaziz 56a236870e modified: main.go
add getUser hander
2025-09-25 16:15:41 +05:00
valitovgaziz 0430c0f29f modified: main.go
add context import
2025-09-25 15:33:17 +05:00
valitovgaziz 6b512927a0 modified: main.go
fix loosed breackeds
2025-09-25 15:29:11 +05:00
Gaziz Valitov 93814296fa изменено: Dockerfile
Change to 1.25.1-alpine
2025-09-25 05:38:03 +05:00
valitovgaziz d95ae290a2 modified: Dockerfile
update fix
2025-09-25 05:01:59 +05:00
valitovgaziz da5b839afe modified: Dockerfile
add alpine for Dockerfile
2025-09-25 04:59:32 +05:00
valitovgaziz 2c07fba277 modified: docker-compose.yaml
modified:   go.mod
	modified:   go.sum
	modified:   main.go
add db for rest api
2025-09-25 04:55:27 +05:00
valitovgaziz c2c48f5f9c modified: Dockerfile
new file:   go.sum
add go.sum file in hadle mod
2025-09-25 03:13:42 +05:00
valitovgaziz 61c5a70534 modified: Dockerfile
change docker file, simplifiy it
2025-09-25 03:10:50 +05:00
valitovgaziz 05ef0009bd modified: docker-compose.yaml
change name service
2025-09-25 03:09:50 +05:00
valitovgaziz 42638e1638 new file: serv_golang_rest_api/Dockerfile
modified:   serv_golang_rest_api/docker-compose.yaml
	new file:   serv_golang_rest_api/go.mod
	new file:   serv_golang_rest_api/main.go
build short Golang rest api server in docker compose conteyner
2025-09-25 03:04:53 +05:00
valitovgaziz e7cb1cb6cc deleted: .env
modified:   README.md
	deleted:    node_servers/authserver.js
	deleted:    node_servers/feedbackserver.js
	deleted:    node_servers/techsupportserver.js
	deleted:    scripts/renew.sh
	new file:   serv_golang_rest_api/docker-compose.yaml
	deleted:    serv_keycloak/docker-compose.yaml
	deleted:    serv_keycloak/keycloak/Dockerfile
	deleted:    serv_keycloak/keycloak/start.sh
	deleted:    serv_keycloak/laksjfdhdsa.txt
	deleted:    serv_migration/.env
	deleted:    serv_migration/docker-compose.yml
	deleted:    serv_migration/migrator/Dockerfile
	deleted:    serv_migration/migrator/go.mod
	deleted:    serv_migration/migrator/go.sum
	deleted:    serv_migration/migrator/migrations/20240819162009_create_users_table.sql
	deleted:    serv_migration/migrator/migrations/20240825004755_add_column_role_to_users.sql
	deleted:    serv_postgres/.env
	deleted:    serv_postgres/docker-compose.yaml
	deleted:    serv_zitadel/.env
	deleted:    serv_zitadel/docker-compose.yaml
	modified:   valitovgaziz/html/style.css
delete deprecated files, a lot of files
2025-09-24 05:32:16 +05:00
Gaziz Valitov 5bbb97c0c3 новый файл: auto_restart_services/docker-compose-up.service
add autorestart services
2025-09-23 21:16:31 +05:00
valitovgaziz cd450abd48 modified: valitovgaziz/html/index.html
valitovgaziz services
2025-09-23 20:59:24 +05:00
valitovgaziz d314bf81db modified: begushiybashkir/src/style/style.css
last commit
2025-09-23 20:25:33 +05:00
valitovgaziz e06cc8854c modified: valitovgaziz/html/index.html
modified:   valitovgaziz/html/style.css
edit the footer, valitovgaziz's site
2025-09-23 17:46:44 +05:00
valitovgaziz 410f0cb67c modified: valitovgaziz/html/style.css
add shadow to skill's tag, valitovgaziz's site
2025-09-23 17:12:08 +05:00
valitovgaziz 34ed53b36c modified: begushiybashkir/src/index.html
BegushiyBashkir site change font for head text
2025-09-23 02:15:56 +05:00
valitovgaziz 1d63fc46fc modified: valitovgaziz/html/style.css
change background color in secriont, valitovgaziz's site
2025-09-23 01:05:43 +05:00
valitovgaziz 921d9d4831 modified: valitovgaziz/html/index.html
modified:   valitovgaziz/html/style.css
try to add linear-gradient
2025-09-23 00:59:00 +05:00
valitovgaziz 7552b6f816 modified: valitovgaziz/html/index.html
add links into project's block, valitovgaziz site
2025-09-22 20:48:01 +05:00
valitovgaziz 7aa791ea0a modified: valitovgaziz/html/index.html
valitovgaziz's site, change link
2025-09-22 20:39:34 +05:00
valitovgaziz a6b133a449 modified: valitovgaziz/html/index.html
ValitovGaziz's site i and strong links to github.com
and delete description for this link
2025-09-21 03:18:56 +05:00
valitovgaziz dc9d76619d modified: valitovgaziz/html/index.html
Change the experiance text valitovgaziz's site
2025-09-21 03:07:25 +05:00
valitovgaziz df49d52d49 modified: valitovgaziz/html/index.html
add target _blanck atribut to links into valitovgaziz's site
2025-09-21 03:03:57 +05:00
valitovgaziz 02b7d9c48f modified: valitovgaziz/html/style/social_link.css
add background color
2025-09-21 02:58:54 +05:00
valitovgaziz 2710bf9cca modified: valitovgaziz/html/index.html
modified:   valitovgaziz/html/style/social_link.css
delete background color for prod
2025-09-20 07:00:37 +05:00
valitovgaziz 5c35ed9128 Merge branch 'set_keycloak' of https://github.com/valitovgaziz/tp into set_keycloak
That's merge and updated upstream into a topic branch
2025-09-17 05:14:03 +05:00
valitovgaziz 59bac20c2c modified: valitovgaziz/html/index.html
change text about into valitovgaziz site
2025-09-17 05:12:39 +05:00
valitovgaziz 1d98790826 modified: begushiybashkir/src/index.html
new file:   begushiybashkir/src/photo/Roshim2025_3.png
	modified:   begushiybashkir/src/style/results.css
	modified:   begushiybashkir/src/style/style.css
set backgournd into achivement and set results into achivement boxes
2025-09-16 15:37:22 +05:00
valitovgaziz 7eacf05c1b new file: begushiybashkir/src/photo/RosHim2025_16_9.jpg
new file:   begushiybashkir/src/photo/UMM2025.png
	modified:   begushiybashkir/src/style/style.css
change main photo on header (background)
2025-09-16 14:34:13 +05:00
valitovgaziz daa261ee6e new file: serv_zitadel/.env
new file:   serv_zitadel/docker-compose.yaml
add files for zitadel
2025-09-16 13:41:03 +05:00
valitovgaziz 685d9c5312 modified: valitovgaziz/html/index.html
modified:   valitovgaziz/html/style.css
	new file:   valitovgaziz/html/style/social_link.css
add socila media links into valitovgaziz's site
2025-09-14 20:53:31 +05:00
valitovgaziz 33c3717ee0 modified: valitovgaziz/html/index.html
insert the preferred IdE into the skills section
2025-09-14 20:28:40 +05:00
valitovgaziz 82edc82128 modified: valitovgaziz/html/index.html
add keycword into valitovgaziz site
2025-09-14 20:22:59 +05:00
valitovgaziz ee1941c115 modified: valitovgaziz/html/index.html
fix some text into valitovgaziz site
2025-09-14 20:14:47 +05:00
valitovgaziz 6b4087d220 modified: begushiybashkir/src/index.html
new file:   begushiybashkir/src/photo/slider/slider24.jpg
add new photo about roshim zabeg into begushiyBashkir site
2025-09-14 16:52:04 +05:00
valitovgaziz 4d6f25db22 modified: serv_nginx/nginx/nginx-http.conf
add auth.yalarba.ru domain into conf for listen 80 port
2025-09-10 16:03:05 +05:00
valitovgaziz 95bf5fe583 modified: serv_nginx/nginx/nginx-ssl.conf
delete space
2025-09-10 15:57:54 +05:00
valitovgaziz 2213091657 modified: serv_nginx/nginx/nginx-ssl.conf
modified:   serv_nginx/nginx/switch-config.sh
fix bag into switch-config.sh nginx
add settings into nginx-ssl.conf for auth.yalarba.ru domain
2025-09-10 15:51:50 +05:00
valitovgaziz 3f88ea631c modified: serv_nginx/nginx/switch-config.sh
add auth.yalarba.ru domain into switch-config.sh for nginx
2025-09-10 15:45:46 +05:00
valitovgaziz 33d7d0b5ba modified: serv_keycloak/docker-compose.yaml
change admin pass for admin and add A subdomain into yalarba.ru domain
2025-09-10 15:42:41 +05:00
valitovgaziz d2202e6097 Changes to be committed:
new file:   valitovgaziz/html/images/favicon/code_orange.png
 	modified:   valitovgaziz/html/index.html
add favicone code for valitovgaziz site
2025-09-09 17:59:08 +05:00
valitovgaziz ae02f46f2e modified: begushiybashkir/src/index.html
modified:   begushiybashkir/src/style/style.css
style the button and hero box for main photo
2025-09-09 17:18:47 +05:00
valitovgaziz 9461ce0886 new file: valitovgaziz/html/images/ValitovGaziz/valitovgaziz.jpg
modified:   valitovgaziz/html/index.html
	modified:   valitovgaziz/html/style.css
	new file:   valitovgaziz/html/style/about.css
add photo on flex box css
2025-09-09 16:43:34 +05:00
valitovgaziz 97e0099430 modified: begushiybashkir/src/index.html
modified:   begushiybashkir/src/style/style.css
fix bag width into schedule, add box-shadow for body
2025-09-09 14:31:12 +05:00
valitovgaziz b16629afea modified: begushiybashkir/src/index.html
new file:   begushiybashkir/src/photo/slider/slider23.png
	modified:   valitovgaziz/html/index.html
add UMM 2025 photo, change position slider
2025-09-09 14:04:20 +05:00
valitovgaziz 035d35c4c6 modified: begushiybashkir/src/index.html
modified:   begushiybashkir/src/yandex_ee0d6f6af1479298.html
2025-09-08 13:36:31 +05:00
valitovgaziz 9724ee949f modified: valitovgaziz/html/index.html
modified:   valitovgaziz/html/style.css
add info about the yalarba invest project
2025-09-04 19:38:28 +05:00
valitovgaziz 3b74861ceb modified: begushiybashkir/src/index.html
new file:   begushiybashkir/src/photo/logo/LogoNew.png
	new file:   begushiybashkir/src/style/Logo/write-log.css
	modified:   begushiybashkir/src/style/style.css
add write logo and img logo into header of site
2025-09-01 16:27:50 +05:00
valitovgaziz 47c012fb4c modified: begushiybashkir/src/index.html
new file:   begushiybashkir/src/style/fonts.css
	new file:   begushiybashkir/src/style/fonts/Lobster-Regular.ttf
	modified:   begushiybashkir/src/style/style.css
add font for logo
2025-09-01 15:52:49 +05:00
valitovgaziz bb648972e6 modified: begushiybashkir/src/index.html
new file:   "begushiybashkir/src/photo/logo/\320\233\320\276\320\263\320\276\320\235\320\260\320\277\320\270\321\201\320\260\321\202\320\265\320\273\321\214\320\275\321\213\320\271.png"
add the log on top of site
2025-09-01 15:43:21 +05:00
valitovgaziz 16ceb32d29 modified: begushiybashkir/src/index.html
delete github.com link
2025-08-28 02:53:34 +05:00
valitovgaziz c3a29a5346 modified: serv_nginx/nginx/switch-config.sh
revert midifaers to only one check when up conteiner
2025-08-28 01:53:29 +05:00
valitovgaziz 60d50b4279 modified: serv_nginx/.env
fix bag into naming environment variables
2025-08-28 01:41:50 +05:00
valitovgaziz 39cbbb6b77 modified: serv_nginx/nginx/switch-config.sh
set script nginx configs for check and change setting every one minute
2025-08-28 01:27:55 +05:00
valitovgaziz 9a9d80103d modified: serv_nginx/certbot/scripts/init-certbot.sh
fix name certs
2025-08-28 01:22:44 +05:00
valitovgaziz 173f55159c modified: serv_nginx/certbot/scripts/checkRenewCerts.sh
add fifth scritp into main function
2025-08-28 01:17:34 +05:00
valitovgaziz 2dac8fbf7f modified: serv_nginx/.env
rename and save one name for rename BBlatin doman
2025-08-28 01:15:47 +05:00
valitovgaziz 4c599f4fdd modified: serv_nginx/nginx/Dockerfile
modified:   serv_nginx/nginx/nginx-http.conf
modifay dockerfile for get selfsine dummy certificate
add redirect from https to http before getting all certs
2025-08-28 00:48:32 +05:00
valitovgaziz 3789a850c2 modified: serv_nginx/nginx/nginx-ssl.conf
set config begushiybashkir.ru latin
2025-08-28 00:34:37 +05:00
valitovgaziz e16624af08 modified: serv_nginx/certbot/scripts/checkRenewCerts.sh
add begushiybashkir.ru domain into this file
2025-08-28 00:17:00 +05:00
valitovgaziz 4dc13d3a64 modified: serv_nginx/.env
fix bag in name .env begushibashkir.ru DOMAINS
2025-08-28 00:04:06 +05:00
valitovgaziz f89fecacf0 modified: serv_nginx/nginx/nginx-http.conf
fix the default config one for nginx server
2025-08-28 00:01:29 +05:00
valitovgaziz 50ab8a3efa modified: serv_nginx/.env
modified:   serv_nginx/certbot/scripts/init-certbot.sh
	new file:   serv_nginx/certbot/scripts/renewBegushiyBAshkirLatin.sh
	modified:   serv_nginx/nginx/nginx-ssl.conf
	modified:   serv_nginx/nginx/switch-config.sh
begushiybashkir.ru add to nginx and certbot for getting certs and
working with begushiybashkir.ru site too
2025-08-27 23:51:23 +05:00
valitovgaziz f8ff70f5e3 modified: begushiybashkir/src/index.html
renamed:    "begushiybashkir/src/photo/logo/\320\233\320\276\320\263\320\276\320\221\320\2212.png" -> begushiybashkir/src/photo/logo/Logo.png
Change name logo
2025-08-26 23:51:28 +05:00
valitovgaziz 81b65ad87b modified: begushiybashkir/src/index.html
new file:   "begushiybashkir/src/photo/logo/\320\233\320\276\320\263\320\276\320\221\320\2212.png"
add favicon logo
2025-08-26 21:28:12 +05:00
valitovgaziz ce695f486f modified: begushiybashkir/src/style/style.css
fix min-width
2025-08-26 19:50:17 +05:00
valitovgaziz 809282c273 modified: begushiybashkir/src/index.html
time in wednesday
2025-08-26 19:17:58 +05:00
valitovgaziz 38652bad50 modified: begushiybashkir/src/index.html
modified:   serv_keycloak/docker-compose.yaml
	new file:   serv_keycloak/laksjfdhdsa.txt
modify the schedule
2025-08-26 19:14:47 +05:00
valitovgaziz ab4a79e63b modified: serv_keycloak/docker-compose.yaml
delete driver bridge
2025-08-25 11:19:39 +05:00
valitovgaziz 3de7b92ecc modified: serv_keycloak/docker-compose.yaml
add networkd for keycloak
2025-08-25 11:18:13 +05:00
valitovgaziz 0c1b472b98 new file: serv_spa/spa/vue/dist/googleef5054d37f782f91.html
add google's verifaing file
2025-08-25 00:36:17 +05:00
valitovgaziz 4fc3625075 new file: begushiybashkir/src/googleef5054d37f782f91.html
add google verification file
2025-08-25 00:32:38 +05:00
valitovgaziz 8d78fe4543 new file: begushiybashkir/src/yandex_ee0d6f6af1479298.html
add yandex verifaing file
2025-08-25 00:12:37 +05:00
valitovgaziz 3c07782a2b new file: serv_spa/spa/vue/dist/yandex_64282c87685452a7.html
add index file for yandex
2025-08-25 00:06:52 +05:00
valitovgaziz 459eed9fb1 modified: begushiybashkir/src/index.html
delete Akhmadullina Guzel info
2025-08-24 23:34:06 +05:00
valitovgaziz 70afc3082e modified: serv_keycloak/docker-compose.yaml
fix bag
2025-08-22 04:35:41 +05:00
valitovgaziz 00aef0248f modified: docker-compose.yaml
modified:   serv_keycloak/docker-compose.yaml
	modified:   serv_postgres/docker-compose.yaml
add kk_db to serv_keycloak
2025-08-22 04:27:18 +05:00
valitovgaziz 2e985f1f5e modified: docker-compose.yaml
modified:   serv_keycloak/docker-compose.yaml
	new file:   serv_postgres/.env
	new file:   serv_postgres/docker-compose.yaml
	modified:   serv_rest_api/.env
	modified:   serv_rest_api/docker-compose.yml
Create postgres serv
2025-08-22 04:14:53 +05:00
valitovgaziz 260c84ecd2 modified: serv_keycloak/docker-compose.yaml
delete internals network
2025-08-22 04:07:08 +05:00
valitovgaziz 98b8ca4fdf new file: serv_keycloak/docker-compose.yaml
renamed:    keycloak/Dockerfile -> serv_keycloak/keycloak/Dockerfile
	renamed:    keycloak/start.sh -> serv_keycloak/keycloak/start.sh
moove keycloak files
2025-08-22 04:05:54 +05:00
valitovgaziz d9a7bf4959 modified: begushiybashkir/src/index.html
modified:   begushiybashkir/src/scripts/script.js
	modified:   begushiybashkir/src/style/mobile.css
	modified:   begushiybashkir/src/style/slider.css
add controls button to slider
2025-08-20 20:09:06 +05:00
valitovgaziz c8e0252d35 modified: begushiybashkir/README.md
modified:   begushiybashkir/src/index.html
add keyword and description into head
2025-08-20 19:42:32 +05:00
valitovgaziz e2216e63b8 modified: begushiybashkir/README.md
SEO optimizing plan
2025-08-20 18:12:31 +05:00
valitovgaziz bbaad513bf modified: begushiybashkir/src/index.html
modified:   begushiybashkir/src/photo/slider/slider1.jpg
	modified:   begushiybashkir/src/photo/slider/slider10.jpg
	modified:   begushiybashkir/src/photo/slider/slider11.jpg
	modified:   begushiybashkir/src/photo/slider/slider12.jpg
	modified:   begushiybashkir/src/photo/slider/slider13.jpg
	modified:   begushiybashkir/src/photo/slider/slider14.jpg
	modified:   begushiybashkir/src/photo/slider/slider15.jpg
	modified:   begushiybashkir/src/photo/slider/slider16.jpg
	modified:   begushiybashkir/src/photo/slider/slider17.jpg
	modified:   begushiybashkir/src/photo/slider/slider18.jpg
	modified:   begushiybashkir/src/photo/slider/slider19.jpg
	modified:   begushiybashkir/src/photo/slider/slider2.jpg
	modified:   begushiybashkir/src/photo/slider/slider20.jpg
	modified:   begushiybashkir/src/photo/slider/slider21.jpg
	modified:   begushiybashkir/src/photo/slider/slider22.jpg
	deleted:    begushiybashkir/src/photo/slider/slider3.jpg
	modified:   begushiybashkir/src/photo/slider/slider4.jpg
	modified:   begushiybashkir/src/photo/slider/slider5.jpg
	modified:   begushiybashkir/src/photo/slider/slider6.jpg
	modified:   begushiybashkir/src/photo/slider/slider7.jpg
	modified:   begushiybashkir/src/photo/slider/slider8.jpg
	modified:   begushiybashkir/src/photo/slider/slider9.jpg
Add edited photo to slider, delete slider3 photo
2025-08-19 15:19:31 +05:00
valitovgaziz 922cc4281f modified: begushiybashkir/src/index.html
Add to slider 22 photo
2025-08-19 14:31:01 +05:00
valitovgaziz abcb34ec18 modified: begushiybashkir/allPhoto/mainPhoto.jpg
deleted:    begushiybashkir/allPhoto/photo_2025-08-13_22-22-55.jpg
	renamed:    begushiybashkir/allPhoto/4qwz9_ZiZpU6HVwVa0gYxavRr2vtz5GzONPblfJFkmLkCBg7u-U2f_jILQWRpb4hDnnYuVHs-JsizxR0n_RYgG2M.jpg -> begushiybashkir/allPhoto/slider1.jpg
	renamed:    begushiybashkir/allPhoto/photo_2025-08-13_22-22-29.jpg -> begushiybashkir/allPhoto/slider10.jpg
	renamed:    begushiybashkir/allPhoto/photo_2025-08-13_22-22-30.jpg -> begushiybashkir/allPhoto/slider11.jpg
	renamed:    begushiybashkir/allPhoto/photo_2025-08-13_22-22-31.jpg -> begushiybashkir/allPhoto/slider12.jpg
	renamed:    begushiybashkir/allPhoto/photo_2025-08-13_22-22-34.jpg -> begushiybashkir/allPhoto/slider13.jpg
	renamed:    begushiybashkir/allPhoto/photo_2025-08-13_22-22-36.jpg -> begushiybashkir/allPhoto/slider14.jpg
	renamed:    begushiybashkir/allPhoto/photo_2025-08-13_22-22-37.jpg -> begushiybashkir/allPhoto/slider15.jpg
	renamed:    begushiybashkir/allPhoto/photo_2025-08-13_22-22-38.jpg -> begushiybashkir/allPhoto/slider16.jpg
	renamed:    begushiybashkir/allPhoto/photo_2025-08-13_22-22-39.jpg -> begushiybashkir/allPhoto/slider17.jpg
	renamed:    begushiybashkir/allPhoto/photo_2025-08-13_22-22-41.jpg -> begushiybashkir/allPhoto/slider18.jpg
	renamed:    begushiybashkir/allPhoto/photo_2025-08-13_22-22-43.jpg -> begushiybashkir/allPhoto/slider19.jpg
	renamed:    begushiybashkir/allPhoto/50geYH7D5Sdv0JyVsCzwXOwf9S0AZY-VfEz2dkUjIC8iqf0i90_1TWpbkAalCkmk_NzTAD978-m9dK4481sH1Bno.jpg -> begushiybashkir/allPhoto/slider2.jpg
	renamed:    begushiybashkir/allPhoto/photo_2025-08-13_22-22-44.jpg -> begushiybashkir/allPhoto/slider20.jpg
	renamed:    begushiybashkir/allPhoto/photo_2025-08-13_22-22-47.jpg -> begushiybashkir/allPhoto/slider21.jpg
	renamed:    begushiybashkir/allPhoto/photo_2025-08-13_22-22-50.jpg -> begushiybashkir/allPhoto/slider22.jpg
	new file:   begushiybashkir/allPhoto/slider3.jpg
	renamed:    begushiybashkir/allPhoto/MainPhoto1.jpg -> begushiybashkir/allPhoto/slider4.jpg
	renamed:    begushiybashkir/allPhoto/MainPhoto2.jpg -> begushiybashkir/allPhoto/slider5.jpg
	renamed:    begushiybashkir/allPhoto/MainPhoto3.jpg -> begushiybashkir/allPhoto/slider6.jpg
	renamed:    begushiybashkir/allPhoto/MainPhoto5.jpg -> begushiybashkir/allPhoto/slider7.jpg
	renamed:    begushiybashkir/allPhoto/photo_2025-08-13_22-22-27.jpg -> begushiybashkir/allPhoto/slider8.jpg
	renamed:    begushiybashkir/allPhoto/photo_2025-08-13_22-22-28.jpg -> begushiybashkir/allPhoto/slider9.jpg
	renamed:    begushiybashkir/allPhoto/-IpB6SO4w3czu57WpHRILQecfpnfYl--XAi0dLsktbPDg0HRthR1yX_xrc6yIJ4fs0vsXpxnBl40K_7MArdCHv5e.jpg -> begushiybashkir/allPhoto/zagir/-IpB6SO4w3czu57WpHRILQecfpnfYl--XAi0dLsktbPDg0HRthR1yX_xrc6yIJ4fs0vsXpxnBl40K_7MArdCHv5e.jpg
	renamed:    begushiybashkir/allPhoto/34xV0jF7No0LV-4tMT4V8YVp9AhGNqramMwxKR5TzjR857GuSiSHghxPQgg0UXg7HPgwSQ5weoZuCAiYus7XCaSM.jpg -> begushiybashkir/allPhoto/zagir/34xV0jF7No0LV-4tMT4V8YVp9AhGNqramMwxKR5TzjR857GuSiSHghxPQgg0UXg7HPgwSQ5weoZuCAiYus7XCaSM.jpg
	renamed:    begushiybashkir/allPhoto/7R3tXacvNpZfpjn-3M-Zq78kweKe5t_bUUCLynm7b9iwKNPZqyqFQhevjqitDn-Q8GRpGJZSwecrn-Gg2AcaKfnK.jpg -> begushiybashkir/allPhoto/zagir/7R3tXacvNpZfpjn-3M-Zq78kweKe5t_bUUCLynm7b9iwKNPZqyqFQhevjqitDn-Q8GRpGJZSwecrn-Gg2AcaKfnK.jpg
renamed:    begushiybashkir/allPhoto/F8Tn7pi0n9vgC5p1xtZuVJB9WTFdgMqCa6d4Pcx1LvCL9_SP6YMOiNx3jfPu5365h7C4JIvklQ6DFQ7o9pWI6n1y.jpg -> begushiybashkir/allPhoto/zagir/F8Tn7pi0n9vgC5p1xtZuVJB9WTFdgMqCa6d4Pcx1LvCL9_SP6YMOiNx3jfPu5365h7C4JIvklQ6DFQ7o9pWI6n1y.jpg
	renamed:    begushiybashkir/allPhoto/IuIO58XcgyITp7u-nxv98_giL9RAPqA0Ou64Hv-RSccSBqVaJ4tF8ttI-XnMb57IdxWZOYAr-tfOr2G8-dDksxOT.jpg -> begushiybashkir/allPhoto/zagir/IuIO58XcgyITp7u-nxv98_giL9RAPqA0Ou64Hv-RSccSBqVaJ4tF8ttI-XnMb57IdxWZOYAr-tfOr2G8-dDksxOT.jpg
	renamed:    begushiybashkir/allPhoto/JVKp07ubApAxESkcLN7Y44SCpclHDAJAre-bpEMeZfqW5h4Jvaw-czUSC8CpR5EbZzYtaVkE-vEuSx15BvwG2Ain.jpg -> begushiybashkir/allPhoto/zagir/JVKp07ubApAxESkcLN7Y44SCpclHDAJAre-bpEMeZfqW5h4Jvaw-czUSC8CpR5EbZzYtaVkE-vEuSx15BvwG2Ain.jpg
	renamed:    begushiybashkir/allPhoto/MBt1Fi50c-0swicDcompcDMau69AtnpOHf4ww9M9jrULUtTSaMeadyecnmqzw8Xsmpon3m2mHjzYK3VCXKCsOcL2.jpg -> begushiybashkir/allPhoto/zagir/MBt1Fi50c-0swicDcompcDMau69AtnpOHf4ww9M9jrULUtTSaMeadyecnmqzw8Xsmpon3m2mHjzYK3VCXKCsOcL2.jpg
enamed:    begushiybashkir/allPhoto/_HPWM2Ok1DyxksDiHkOtNQdgCqkO8wWdLaK2RBPNbu6liCUwiiivqwtR6P1y12ATUbElNVHkVjiM94ZzOiH49maO.jpg -> begushiybashkir/allPhoto/zagir/_HPWM2Ok1DyxksDiHkOtNQdgCqkO8wWdLaK2RBPNbu6liCUwiiivqwtR6P1y12ATUbElNVHkVjiM94ZzOiH49maO.jpg
	renamed:    begushiybashkir/allPhoto/bwnopBq78b6r0HeJK2FLskwlk2soeqV8AC-ve10-WH8XX8yy2aD5d8pc57pJnu8BcdZT8xneCGl7FpPTiCaGePQn.jpg -> begushiybashkir/allPhoto/zagir/bwnopBq78b6r0HeJK2FLskwlk2soeqV8AC-ve10-WH8XX8yy2aD5d8pc57pJnu8BcdZT8xneCGl7FpPTiCaGePQn.jpg
	renamed:    begushiybashkir/allPhoto/eMsY_q4QXF58mRumDerwjaj82MY37q2PBdkZaLIZegcl18HgzifPvjaD0hIyPbNDOde2IZZNnV79ydbeYiCmVIPi.jpg -> begushiybashkir/allPhoto/zagir/eMsY_q4QXF58mRumDerwjaj82MY37q2PBdkZaLIZegcl18HgzifPvjaD0hIyPbNDOde2IZZNnV79ydbeYiCmVIPi.jpg
	renamed:    begushiybashkir/allPhoto/eo3EVEbCACsiPNXupOsaIyVOTVLbCqWSm68NSutgeccGBdgOSbctQ0Gzr2I-fMjFsU2KADbVHQ3bwk_j6S7HAygl.jpg -> begushiybashkir/allPhoto/zagir/eo3EVEbCACsiPNXupOsaIyVOTVLbCqWSm68NSutgeccGBdgOSbctQ0Gzr2I-fMjFsU2KADbVHQ3bwk_j6S7HAygl.jpg
	renamed:    begushiybashkir/allPhoto/iZQJZ4QSXeVvbg_bn-tnFM6o5qburXkdR1CEAruOiJzH8NORFIg8YgMqpGtdzNdhCxDsIJelIt3yUUmR9hkAEZK8.jpg -> begushiybashkir/allPhoto/zagir/iZQJZ4QSXeVvbg_bn-tnFM6o5qburXkdR1CEAruOiJzH8NORFIg8YgMqpGtdzNdhCxDsIJelIt3yUUmR9hkAEZK8.jpg
	renamed:    begushiybashkir/allPhoto/koQkc-8N48CIK5Eyci_JTitKfy3Mj9WkTEv4755clOB_NCXKglQsIdIn__a3SNPFhvQapsayHRTUa0o8khpwordS.jpg -> begushiybashkir/allPhoto/zagir/koQkc-8N48CIK5Eyci_JTitKfy3Mj9WkTEv4755clOB_NCXKglQsIdIn__a3SNPFhvQapsayHRTUa0o8khpwordS.jpg
	renamed:    begushiybashkir/allPhoto/mOZeq1KR1Umcyc4NO-YIilQRSBw7O--D-WYCfY0JBmmp7Zq2uPOVRMw64HR7-KXqCDiZIi8terx1AQXLhE-AZf9u.jpg -> begushiybashkir/allPhoto/zagir/mOZeq1KR1Umcyc4NO-YIilQRSBw7O--D-WYCfY0JBmmp7Zq2uPOVRMw64HR7-KXqCDiZIi8terx1AQXLhE-AZf9u.jpg
	modified:   begushiybashkir/src/index.html
	new file:   begushiybashkir/src/photo/slider/slider1.jpg
	new file:   begushiybashkir/src/photo/slider/slider10.jpg
	new file:   begushiybashkir/src/photo/slider/slider11.jpg
	new file:   begushiybashkir/src/photo/slider/slider12.jpg
	new file:   begushiybashkir/src/photo/slider/slider13.jpg
	new file:   begushiybashkir/src/photo/slider/slider14.jpg
	new file:   begushiybashkir/src/photo/slider/slider15.jpg
	new file:   begushiybashkir/src/photo/slider/slider16.jpg
	new file:   begushiybashkir/src/photo/slider/slider17.jpg
	new file:   begushiybashkir/src/photo/slider/slider18.jpg
	new file:   begushiybashkir/src/photo/slider/slider19.jpg
	new file:   begushiybashkir/src/photo/slider/slider2.jpg
	new file:   begushiybashkir/src/photo/slider/slider20.jpg
	new file:   begushiybashkir/src/photo/slider/slider21.jpg
	new file:   begushiybashkir/src/photo/slider/slider22.jpg
	new file:   begushiybashkir/src/photo/slider/slider3.jpg
	new file:   begushiybashkir/src/photo/slider/slider4.jpg
	new file:   begushiybashkir/src/photo/slider/slider5.jpg
	new file:   begushiybashkir/src/photo/slider/slider6.jpg
	new file:   begushiybashkir/src/photo/slider/slider7.jpg
	new file:   begushiybashkir/src/photo/slider/slider8.jpg
	new file:   begushiybashkir/src/photo/slider/slider9.jpg
	modified:   begushiybashkir/src/scripts/script.js
	modified:   begushiybashkir/src/style/results.css
	new file:   begushiybashkir/src/style/slider.css
	modified:   begushiybashkir/src/style/style.css
Add photo and add sliders
2025-08-19 12:49:40 +05:00
valitovgaziz 45f4350fdb modified: begushiybashkir/src/index.html
new file:   begushiybashkir/src/style/results.css
	modified:   begushiybashkir/src/style/style.css
add section results and styles results
2025-08-19 12:07:55 +05:00
valitovgaziz 11f7f47af2 new file: begushiybashkir/src/photo/MainPhoto10.png
deleted:    begushiybashkir/src/photo/MainPhoto11.png
	modified:   begushiybashkir/src/style/style.css
change photo and name mainPhoto
2025-08-17 23:49:20 +05:00
valitovgaziz 95d4fa8347 modified: begushiybashkir/src/style/style.css
change name into file
2025-08-17 23:48:03 +05:00
valitovgaziz f689f65d3f renamed: begushiybashkir/src/photo/MainPhoto10.png -> begushiybashkir/src/photo/MainPhoto11.png
change MainPhoto11
2025-08-17 23:46:38 +05:00
valitovgaziz a147788cb9 modified: begushiybashkir/src/index.html
modified:   begushiybashkir/src/scripts/saveContacts.js
add Zagir contacts
2025-08-17 23:38:28 +05:00
valitovgaziz bc637a6414 renamed: begushiybashkir/allPhoto/photo_2025-08-13_22-22-45.jpg -> begushiybashkir/allPhoto/MainPhoto1.jpg
renamed:    begushiybashkir/allPhoto/photo_2025-08-13_22-22-45 (2).jpg -> begushiybashkir/allPhoto/MainPhoto2.jpg
	renamed:    begushiybashkir/allPhoto/photo_2025-08-13_22-22-42.jpg -> begushiybashkir/allPhoto/MainPhoto3.jpg
	new file:   begushiybashkir/allPhoto/MainPhoto5.jpg
	deleted:    begushiybashkir/allPhoto/photo_2025-08-13_22-22-40.jpg
	new file:   begushiybashkir/src/photo/MainPhoto10.png
	modified:   begushiybashkir/src/style/style.css
change mainPhoto
2025-08-17 23:17:13 +05:00
valitovgaziz ffd4e6f085 modified: begushiybashkir/README.md
new file:   begushiybashkir/allPhoto/photo_2025-08-13_22-22-27.jpg
	new file:   begushiybashkir/allPhoto/photo_2025-08-13_22-22-28.jpg
	new file:   begushiybashkir/allPhoto/photo_2025-08-13_22-22-29.jpg
	new file:   begushiybashkir/allPhoto/photo_2025-08-13_22-22-30.jpg
	new file:   begushiybashkir/allPhoto/photo_2025-08-13_22-22-31.jpg
	new file:   begushiybashkir/allPhoto/photo_2025-08-13_22-22-34.jpg
	new file:   begushiybashkir/allPhoto/photo_2025-08-13_22-22-36.jpg
	new file:   begushiybashkir/allPhoto/photo_2025-08-13_22-22-37.jpg
	new file:   begushiybashkir/allPhoto/photo_2025-08-13_22-22-38.jpg
	new file:   begushiybashkir/allPhoto/photo_2025-08-13_22-22-39.jpg
	new file:   begushiybashkir/allPhoto/photo_2025-08-13_22-22-40.jpg
	new file:   begushiybashkir/allPhoto/photo_2025-08-13_22-22-41.jpg
	new file:   begushiybashkir/allPhoto/photo_2025-08-13_22-22-42.jpg
	new file:   begushiybashkir/allPhoto/photo_2025-08-13_22-22-43.jpg
	new file:   begushiybashkir/allPhoto/photo_2025-08-13_22-22-44.jpg
	new file:   begushiybashkir/allPhoto/photo_2025-08-13_22-22-45 (2).jpg
	new file:   begushiybashkir/allPhoto/photo_2025-08-13_22-22-45.jpg
	new file:   begushiybashkir/allPhoto/photo_2025-08-13_22-22-47.jpg
	new file:   begushiybashkir/allPhoto/photo_2025-08-13_22-22-50.jpg
	new file:   begushiybashkir/allPhoto/photo_2025-08-13_22-22-55.jpg
	new file:   begushiybashkir/allPhoto/zagir/photo_2025-08-13_23-13-25.jpg
	new file:   begushiybashkir/allPhoto/zagir/photo_2025-08-13_23-13-26.jpg
	new file:   begushiybashkir/allPhoto/zagir/photo_2025-08-13_23-13-27.jpg
add photo from Zagir
2025-08-17 22:37:34 +05:00
valitovgaziz bb5e8741a6 modified: begushiybashkir/src/index.html
delete space
2025-08-17 20:17:46 +05:00
valitovgaziz 4e16c6b20a modified: begushiybashkir/src/index.html
fix bag q
2025-08-13 18:34:12 +05:00
valitovgaziz 20230c4b0a modified: begushiybashkir/src/index.html
renamed:    begushiybashkir/src/scripts.js/callPhone.js -> begushiybashkir/src/scripts/callPhone.js
	new file:   begushiybashkir/src/scripts/saveContacts.js
	renamed:    begushiybashkir/src/scripts.js/script.js -> begushiybashkir/src/scripts/script.js
add save contcts button
2025-08-13 18:32:53 +05:00
valitovgaziz a4683abf0e modified: begushiybashkir/src/index.html
change photo Zagir
2025-08-13 18:02:42 +05:00
valitovgaziz 2e7cb6a3bf modified: begushiybashkir/src/index.html
fix year date
2025-08-13 12:07:36 +05:00
valitovgaziz 1bc1e1c642 new file: begushiybashkir/README.md
new file:   begushiybashkir/allPhoto/4qwz9_ZiZpU6HVwVa0gYxavRr2vtz5GzONPblfJFkmLkCBg7u-U2f_jILQWRpb4hDnnYuVHs-JsizxR0n_RYgG2M.jpg
	new file:   begushiybashkir/allPhoto/logo.jpg
	new file:   begushiybashkir/allPhoto/sFKpvr_J9QgeFZGJTNtaYAO_wteKLoZVFEFo7EnwzsJktlK1iLt0IZ6ZzcNKsqqikyEuSX8JBpRRlwJQzp7GOpVZ.jpg
	modified:   begushiybashkir/src/index.html
add photo and links into reade.md file
2025-08-13 12:06:21 +05:00
valitovgaziz 9430130215 modified: begushiybashkir/src/index.html
modified:   begushiybashkir/src/style/style.css
add card to schedule
2025-08-13 08:08:20 +05:00
valitovgaziz 77672dc6ec renamed: begushiybashkir/allPhoto/AtylqrEsbMCdt79zyh8qtA4Z9zeBM9kpp6T7NGukNUnm1aYHA6D52sQNmRzTE18LIWiBwdk9oZllNZWihCPZJFXG.jpg -> begushiybashkir/allPhoto/mainPhoto.jpg
modified:   begushiybashkir/src/index.html
	new file:   begushiybashkir/src/photo/mainPhoto.jpg
	new file:   begushiybashkir/src/photo/sprinting-athletics-man-scaled.jpg
	new file:   begushiybashkir/src/style/descktop.css
	new file:   begushiybashkir/src/style/mobile.css
	modified:   begushiybashkir/src/style/style.css
	new file:   begushiybashkir/src/style/tablet.css
delete table and add card for each days in week
2025-08-13 07:50:10 +05:00
valitovgaziz 4937be9d35 modified: begushiybashkir/src/style/style.css
delete border
2025-08-13 03:50:46 +05:00
valitovgaziz 4d0fc6645b modified: begushiybashkir/src/index.html
modified:   begushiybashkir/src/scripts.js/script.js
	modified:   begushiybashkir/src/style/style.css
add second button for call and save contacts
2025-08-13 03:44:32 +05:00
valitovgaziz 88a5ab8be6 modified: begushiybashkir/src/index.html
new file:   begushiybashkir/src/scripts.js/callPhone.js
	modified:   begushiybashkir/src/scripts.js/script.js
add call button with call script
2025-08-13 02:14:16 +05:00
Gaziz Valitov 7538ee30ca изменено: README.md
last moov into readme.md
2025-08-12 20:32:13 +05:00
valitovgaziz 50b73dff77 modified: begushiybashkir/src/style/style.css
add min-width conteiner
2025-08-11 15:19:58 +05:00
valitovgaziz dc507da73d modified: begushiybashkir/src/index.html
new file:   begushiybashkir/src/scripts.js/script.js
	new file:   begushiybashkir/src/style/input.css
	modified:   begushiybashkir/src/style/style.css
add onload window func, divice style intput
2025-08-11 14:45:25 +05:00
valitovgaziz 33cc6ef830 deleted: begushiybashkir/src/assets/bashkorsa.md
modified:   begushiybashkir/src/index.html
	deleted:    begushiybashkir/src/photo/RunnerHome.jpg
	deleted:    begushiybashkir/src/photo/ZagirTrainer2.jpg
	deleted:    begushiybashkir/src/script.js
	new file:   begushiybashkir/src/style/style.css
	deleted:    begushiybashkir/src/styles/descktopStyle.css
	deleted:    begushiybashkir/src/styles/mobileStyle.css
	deleted:    begushiybashkir/src/styles/style.css
	deleted:    begushiybashkir/src/styles/tabletStyle.css
add new site with green style
2025-08-11 07:59:18 +05:00
valitovgaziz c7b17b6765 modified: begushiybashkir/src/index.html
modified:   begushiybashkir/src/styles/mobileStyle.css
	modified:   begushiybashkir/src/styles/style.css
last commit
2025-08-11 07:57:45 +05:00
valitovgaziz 8157ce49b8 modified: begushiybashkir/src/index.html
modified:   begushiybashkir/src/styles/style.css
text on top of img
2025-08-09 05:14:10 +05:00
valitovgaziz b5bac2ad61 modified: begushiybashkir/src/index.html
renamed:    begushiybashkir/allPhoto/t-bRlBHT178NfhzrUioK0mGtGW5onpiR_BBPd8imEDMZgHj_iyeV5wl9HN-ql_IlzQjnTz_YMb1ktJLHKctMQpuF.jpg -> begushiybashkir/src/photo/ZagirTrainer3.jpg
change photo
2025-08-09 04:26:11 +05:00
valitovgaziz 72acf7d361 modified: serv_nginx/nginx/nginx-ssl.conf
change server name to xn--80abahjtcfl5d0a8di.xn--p1ai
2025-08-09 00:50:37 +05:00
valitovgaziz adf4a12775 modified: serv_nginx/certbot/config/certbot.ini
delete domens from certbot.ini file config
2025-08-09 00:30:52 +05:00
valitovgaziz 9df6379484 renamed: ZagirAminev/allPhoto/-IpB6SO4w3czu57WpHRILQecfpnfYl--XAi0dLsktbPDg0HRthR1yX_xrc6yIJ4fs0vsXpxnBl40K_7MArdCHv5e.jpg -> begushiybashkir/allPhoto/-IpB6SO4w3czu57WpHRILQecfpnfYl--XAi0dLsktbPDg0HRthR1yX_xrc6yIJ4fs0vsXpxnBl40K_7MArdCHv5e.jpg
renamed:    ZagirAminev/allPhoto/34xV0jF7No0LV-4tMT4V8YVp9AhGNqramMwxKR5TzjR857GuSiSHghxPQgg0UXg7HPgwSQ5weoZuCAiYus7XCaSM.jpg -> begushiybashkir/allPhoto/34xV0jF7No0LV-4tMT4V8YVp9AhGNqramMwxKR5TzjR857GuSiSHghxPQgg0UXg7HPgwSQ5weoZuCAiYus7XCaSM.jpg
	renamed:    ZagirAminev/allPhoto/50geYH7D5Sdv0JyVsCzwXOwf9S0AZY-VfEz2dkUjIC8iqf0i90_1TWpbkAalCkmk_NzTAD978-m9dK4481sH1Bno.jpg -> begushiybashkir/allPhoto/50geYH7D5Sdv0JyVsCzwXOwf9S0AZY-VfEz2dkUjIC8iqf0i90_1TWpbkAalCkmk_NzTAD978-m9dK4481sH1Bno.jpg
	renamed:    ZagirAminev/allPhoto/7R3tXacvNpZfpjn-3M-Zq78kweKe5t_bUUCLynm7b9iwKNPZqyqFQhevjqitDn-Q8GRpGJZSwecrn-Gg2AcaKfnK.jpg -> begushiybashkir/allPhoto/7R3tXacvNpZfpjn-3M-Zq78kweKe5t_bUUCLynm7b9iwKNPZqyqFQhevjqitDn-Q8GRpGJZSwecrn-Gg2AcaKfnK.jpg
	renamed:    ZagirAminev/allPhoto/AtylqrEsbMCdt79zyh8qtA4Z9zeBM9kpp6T7NGukNUnm1aYHA6D52sQNmRzTE18LIWiBwdk9oZllNZWihCPZJFXG.jpg -> begushiybashkir/allPhoto/AtylqrEsbMCdt79zyh8qtA4Z9zeBM9kpp6T7NGukNUnm1aYHA6D52sQNmRzTE18LIWiBwdk9oZllNZWihCPZJFXG.jpg
	renamed:    ZagirAminev/allPhoto/F8Tn7pi0n9vgC5p1xtZuVJB9WTFdgMqCa6d4Pcx1LvCL9_SP6YMOiNx3jfPu5365h7C4JIvklQ6DFQ7o9pWI6n1y.jpg -> begushiybashkir/allPhoto/F8Tn7pi0n9vgC5p1xtZuVJB9WTFdgMqCa6d4Pcx1LvCL9_SP6YMOiNx3jfPu5365h7C4JIvklQ6DFQ7o9pWI6n1y.jpg
	renamed:    ZagirAminev/allPhoto/IuIO58XcgyITp7u-nxv98_giL9RAPqA0Ou64Hv-RSccSBqVaJ4tF8ttI-XnMb57IdxWZOYAr-tfOr2G8-dDksxOT.jpg -> begushiybashkir/allPhoto/IuIO58XcgyITp7u-nxv98_giL9RAPqA0Ou64Hv-RSccSBqVaJ4tF8ttI-XnMb57IdxWZOYAr-tfOr2G8-dDksxOT.jpg
	renamed:    ZagirAminev/allPhoto/JVKp07ubApAxESkcLN7Y44SCpclHDAJAre-bpEMeZfqW5h4Jvaw-czUSC8CpR5EbZzYtaVkE-vEuSx15BvwG2Ain.jpg -> begushiybashkir/allPhoto/JVKp07ubApAxESkcLN7Y44SCpclHDAJAre-bpEMeZfqW5h4Jvaw-czUSC8CpR5EbZzYtaVkE-vEuSx15BvwG2Ain.jpg
	renamed:    ZagirAminev/allPhoto/MBt1Fi50c-0swicDcompcDMau69AtnpOHf4ww9M9jrULUtTSaMeadyecnmqzw8Xsmpon3m2mHjzYK3VCXKCsOcL2.jpg -> begushiybashkir/allPhoto/MBt1Fi50c-0swicDcompcDMau69AtnpOHf4ww9M9jrULUtTSaMeadyecnmqzw8Xsmpon3m2mHjzYK3VCXKCsOcL2.jpg
	renamed:    ZagirAminev/allPhoto/PTILTFDmM9l6UBXoEHrwMUQeq3F61oKqTUVr1nPqMVNg3XjxjC2t463cRmg9UxH_R31U_vgdebAE2xkkbG7nTTK4.jpg -> begushiybashkir/allPhoto/PTILTFDmM9l6UBXoEHrwMUQeq3F61oKqTUVr1nPqMVNg3XjxjC2t463cRmg9UxH_R31U_vgdebAE2xkkbG7nTTK4.jpg
	renamed:    ZagirAminev/allPhoto/VuvtXAIwJk9KkfKfOT6MaJ9D-qJ4TlPfC7R_oeKbMGMoaIko5xj3Jm_X__kuvttLvCitCBmV5jR6kXXPolrGEl_N.jpg -> begushiybashkir/allPhoto/VuvtXAIwJk9KkfKfOT6MaJ9D-qJ4TlPfC7R_oeKbMGMoaIko5xj3Jm_X__kuvttLvCitCBmV5jR6kXXPolrGEl_N.jpg
	renamed:    ZagirAminev/allPhoto/_HPWM2Ok1DyxksDiHkOtNQdgCqkO8wWdLaK2RBPNbu6liCUwiiivqwtR6P1y12ATUbElNVHkVjiM94ZzOiH49maO.jpg -> begushiybashkir/allPhoto/_HPWM2Ok1DyxksDiHkOtNQdgCqkO8wWdLaK2RBPNbu6liCUwiiivqwtR6P1y12ATUbElNVHkVjiM94ZzOiH49maO.jpg
	renamed:    ZagirAminev/allPhoto/bwnopBq78b6r0HeJK2FLskwlk2soeqV8AC-ve10-WH8XX8yy2aD5d8pc57pJnu8BcdZT8xneCGl7FpPTiCaGePQn.jpg -> begushiybashkir/allPhoto/bwnopBq78b6r0HeJK2FLskwlk2soeqV8AC-ve10-WH8XX8yy2aD5d8pc57pJnu8BcdZT8xneCGl7FpPTiCaGePQn.jpg
	renamed:    ZagirAminev/allPhoto/eMsY_q4QXF58mRumDerwjaj82MY37q2PBdkZaLIZegcl18HgzifPvjaD0hIyPbNDOde2IZZNnV79ydbeYiCmVIPi.jpg -> begushiybashkir/allPhoto/eMsY_q4QXF58mRumDerwjaj82MY37q2PBdkZaLIZegcl18HgzifPvjaD0hIyPbNDOde2IZZNnV79ydbeYiCmVIPi.jpg
	renamed:    ZagirAminev/allPhoto/eo3EVEbCACsiPNXupOsaIyVOTVLbCqWSm68NSutgeccGBdgOSbctQ0Gzr2I-fMjFsU2KADbVHQ3bwk_j6S7HAygl.jpg -> begushiybashkir/allPhoto/eo3EVEbCACsiPNXupOsaIyVOTVLbCqWSm68NSutgeccGBdgOSbctQ0Gzr2I-fMjFsU2KADbVHQ3bwk_j6S7HAygl.jpg
	renamed:    ZagirAminev/allPhoto/iZQJZ4QSXeVvbg_bn-tnFM6o5qburXkdR1CEAruOiJzH8NORFIg8YgMqpGtdzNdhCxDsIJelIt3yUUmR9hkAEZK8.jpg -> begushiybashkir/allPhoto/iZQJZ4QSXeVvbg_bn-tnFM6o5qburXkdR1CEAruOiJzH8NORFIg8YgMqpGtdzNdhCxDsIJelIt3yUUmR9hkAEZK8.jpg
	renamed:    ZagirAminev/allPhoto/koQkc-8N48CIK5Eyci_JTitKfy3Mj9WkTEv4755clOB_NCXKglQsIdIn__a3SNPFhvQapsayHRTUa0o8khpwordS.jpg -> begushiybashkir/allPhoto/koQkc-8N48CIK5Eyci_JTitKfy3Mj9WkTEv4755clOB_NCXKglQsIdIn__a3SNPFhvQapsayHRTUa0o8khpwordS.jpg
	renamed:    ZagirAminev/allPhoto/mOZeq1KR1Umcyc4NO-YIilQRSBw7O--D-WYCfY0JBmmp7Zq2uPOVRMw64HR7-KXqCDiZIi8terx1AQXLhE-AZf9u.jpg -> begushiybashkir/allPhoto/mOZeq1KR1Umcyc4NO-YIilQRSBw7O--D-WYCfY0JBmmp7Zq2uPOVRMw64HR7-KXqCDiZIi8terx1AQXLhE-AZf9u.jpg
	renamed:    ZagirAminev/allPhoto/rSi4RU0F0LG87Y3yy9ccRtJILTIqOYgmVSNTzxCqmO-1oHgTBs_Nak2x79gVOWUMI6BEOJbu6__t6rZ818fX3ms7.jpg -> begushiybashkir/allPhoto/rSi4RU0F0LG87Y3yy9ccRtJILTIqOYgmVSNTzxCqmO-1oHgTBs_Nak2x79gVOWUMI6BEOJbu6__t6rZ818fX3ms7.jpg
	renamed:    ZagirAminev/allPhoto/t-bRlBHT178NfhzrUioK0mGtGW5onpiR_BBPd8imEDMZgHj_iyeV5wl9HN-ql_IlzQjnTz_YMb1ktJLHKctMQpuF.jpg -> begushiybashkir/allPhoto/t-bRlBHT178NfhzrUioK0mGtGW5onpiR_BBPd8imEDMZgHj_iyeV5wl9HN-ql_IlzQjnTz_YMb1ktJLHKctMQpuF.jpg
	renamed:    ZagirAminev/src/assets/bashkorsa.md -> begushiybashkir/src/assets/bashkorsa.md
	renamed:    ZagirAminev/src/index.html -> begushiybashkir/src/index.html
	renamed:    ZagirAminev/src/photo/RunnerHome.jpg -> begushiybashkir/src/photo/RunnerHome.jpg
	renamed:    ZagirAminev/src/photo/ZagirTrainer.jpg -> begushiybashkir/src/photo/ZagirTrainer.jpg
	renamed:    ZagirAminev/src/photo/ZagirTrainer2.jpg -> begushiybashkir/src/photo/ZagirTrainer2.jpg
	renamed:    ZagirAminev/src/script.js -> begushiybashkir/src/script.js
	renamed:    ZagirAminev/src/styles/descktopStyle.css -> begushiybashkir/src/styles/descktopStyle.css
	renamed:    ZagirAminev/src/styles/mobileStyle.css -> begushiybashkir/src/styles/mobileStyle.css
	renamed:    ZagirAminev/src/styles/style.css -> begushiybashkir/src/styles/style.css
	renamed:    ZagirAminev/src/styles/tabletStyle.css -> begushiybashkir/src/styles/tabletStyle.css
	modified:   serv_nginx/docker-compose.yml
add change name
2025-08-09 00:04:58 +05:00
valitovgaziz 5ea04ef1d9 modified: serv_nginx/docker-compose.yml
reuse
2025-08-08 23:58:17 +05:00
valitovgaziz 9037ac16d9 modified: serv_nginx/docker-compose.yml
prettify code
2025-08-08 23:55:27 +05:00
valitovgaziz 6642d508ef Changes to be committed:
renamed:    ZagirAminev/src/photo/-IpB6SO4w3czu57WpHRILQecfpnfYl--XAi0dLsktbPDg0HRthR1yX_xrc6yIJ4fs0vsXpxnBl40K_7MArdCHv5e.jpg -> ZagirAminev/allPhoto/-IpB6SO4w3czu57WpHRILQecfpnfYl--XAi0dLsktbPDg0HRthR1yX_xrc6yIJ4fs0vsXpxnBl40K_7MArdCHv5e.jpg
	renamed:    ZagirAminev/src/photo/34xV0jF7No0LV-4tMT4V8YVp9AhGNqramMwxKR5TzjR857GuSiSHghxPQgg0UXg7HPgwSQ5weoZuCAiYus7XCaSM.jpg -> ZagirAminev/allPhoto/34xV0jF7No0LV-4tMT4V8YVp9AhGNqramMwxKR5TzjR857GuSiSHghxPQgg0UXg7HPgwSQ5weoZuCAiYus7XCaSM.jpg
	renamed:    ZagirAminev/src/photo/50geYH7D5Sdv0JyVsCzwXOwf9S0AZY-VfEz2dkUjIC8iqf0i90_1TWpbkAalCkmk_NzTAD978-m9dK4481sH1Bno.jpg -> ZagirAminev/allPhoto/50geYH7D5Sdv0JyVsCzwXOwf9S0AZY-VfEz2dkUjIC8iqf0i90_1TWpbkAalCkmk_NzTAD978-m9dK4481sH1Bno.jpg
	renamed:    ZagirAminev/src/photo/7R3tXacvNpZfpjn-3M-Zq78kweKe5t_bUUCLynm7b9iwKNPZqyqFQhevjqitDn-Q8GRpGJZSwecrn-Gg2AcaKfnK.jpg -> ZagirAminev/allPhoto/7R3tXacvNpZfpjn-3M-Zq78kweKe5t_bUUCLynm7b9iwKNPZqyqFQhevjqitDn-Q8GRpGJZSwecrn-Gg2AcaKfnK.jpg
	renamed:    ZagirAminev/src/photo/AtylqrEsbMCdt79zyh8qtA4Z9zeBM9kpp6T7NGukNUnm1aYHA6D52sQNmRzTE18LIWiBwdk9oZllNZWihCPZJFXG.jpg -> ZagirAminev/allPhoto/AtylqrEsbMCdt79zyh8qtA4Z9zeBM9kpp6T7NGukNUnm1aYHA6D52sQNmRzTE18LIWiBwdk9oZllNZWihCPZJFXG.jpg
	renamed:    ZagirAminev/src/photo/F8Tn7pi0n9vgC5p1xtZuVJB9WTFdgMqCa6d4Pcx1LvCL9_SP6YMOiNx3jfPu5365h7C4JIvklQ6DFQ7o9pWI6n1y.jpg -> ZagirAminev/allPhoto/F8Tn7pi0n9vgC5p1xtZuVJB9WTFdgMqCa6d4Pcx1LvCL9_SP6YMOiNx3jfPu5365h7C4JIvklQ6DFQ7o9pWI6n1y.jpg
	renamed:    ZagirAminev/src/photo/IuIO58XcgyITp7u-nxv98_giL9RAPqA0Ou64Hv-RSccSBqVaJ4tF8ttI-XnMb57IdxWZOYAr-tfOr2G8-dDksxOT.jpg -> ZagirAminev/allPhoto/IuIO58XcgyITp7u-nxv98_giL9RAPqA0Ou64Hv-RSccSBqVaJ4tF8ttI-XnMb57IdxWZOYAr-tfOr2G8-dDksxOT.jpg
	renamed:    ZagirAminev/src/photo/JVKp07ubApAxESkcLN7Y44SCpclHDAJAre-bpEMeZfqW5h4Jvaw-czUSC8CpR5EbZzYtaVkE-vEuSx15BvwG2Ain.jpg -> ZagirAminev/allPhoto/JVKp07ubApAxESkcLN7Y44SCpclHDAJAre-bpEMeZfqW5h4Jvaw-czUSC8CpR5EbZzYtaVkE-vEuSx15BvwG2Ain.jpg
	renamed:    ZagirAminev/src/photo/MBt1Fi50c-0swicDcompcDMau69AtnpOHf4ww9M9jrULUtTSaMeadyecnmqzw8Xsmpon3m2mHjzYK3VCXKCsOcL2.jpg -> ZagirAminev/allPhoto/MBt1Fi50c-0swicDcompcDMau69AtnpOHf4ww9M9jrULUtTSaMeadyecnmqzw8Xsmpon3m2mHjzYK3VCXKCsOcL2.jpg
	renamed:    ZagirAminev/src/photo/PTILTFDmM9l6UBXoEHrwMUQeq3F61oKqTUVr1nPqMVNg3XjxjC2t463cRmg9UxH_R31U_vgdebAE2xkkbG7nTTK4.jpg -> ZagirAminev/allPhoto/PTILTFDmM9l6UBXoEHrwMUQeq3F61oKqTUVr1nPqMVNg3XjxjC2t463cRmg9UxH_R31U_vgdebAE2xkkbG7nTTK4.jpg
	renamed:    ZagirAminev/src/photo/VuvtXAIwJk9KkfKfOT6MaJ9D-qJ4TlPfC7R_oeKbMGMoaIko5xj3Jm_X__kuvttLvCitCBmV5jR6kXXPolrGEl_N.jpg -> ZagirAminev/allPhoto/VuvtXAIwJk9KkfKfOT6MaJ9D-qJ4TlPfC7R_oeKbMGMoaIko5xj3Jm_X__kuvttLvCitCBmV5jR6kXXPolrGEl_N.jpg
	renamed:    ZagirAminev/src/photo/_HPWM2Ok1DyxksDiHkOtNQdgCqkO8wWdLaK2RBPNbu6liCUwiiivqwtR6P1y12ATUbElNVHkVjiM94ZzOiH49maO.jpg -> ZagirAminev/allPhoto/_HPWM2Ok1DyxksDiHkOtNQdgCqkO8wWdLaK2RBPNbu6liCUwiiivqwtR6P1y12ATUbElNVHkVjiM94ZzOiH49maO.jpg
	renamed:    ZagirAminev/src/photo/bwnopBq78b6r0HeJK2FLskwlk2soeqV8AC-ve10-WH8XX8yy2aD5d8pc57pJnu8BcdZT8xneCGl7FpPTiCaGePQn.jpg -> ZagirAminev/allPhoto/bwnopBq78b6r0HeJK2FLskwlk2soeqV8AC-ve10-WH8XX8yy2aD5d8pc57pJnu8BcdZT8xneCGl7FpPTiCaGePQn.jpg
	renamed:    ZagirAminev/src/photo/eMsY_q4QXF58mRumDerwjaj82MY37q2PBdkZaLIZegcl18HgzifPvjaD0hIyPbNDOde2IZZNnV79ydbeYiCmVIPi.jpg -> ZagirAminev/allPhoto/eMsY_q4QXF58mRumDerwjaj82MY37q2PBdkZaLIZegcl18HgzifPvjaD0hIyPbNDOde2IZZNnV79ydbeYiCmVIPi.jpg
	renamed:    ZagirAminev/src/photo/eo3EVEbCACsiPNXupOsaIyVOTVLbCqWSm68NSutgeccGBdgOSbctQ0Gzr2I-fMjFsU2KADbVHQ3bwk_j6S7HAygl.jpg -> ZagirAminev/allPhoto/eo3EVEbCACsiPNXupOsaIyVOTVLbCqWSm68NSutgeccGBdgOSbctQ0Gzr2I-fMjFsU2KADbVHQ3bwk_j6S7HAygl.jpg
	renamed:    ZagirAminev/src/photo/iZQJZ4QSXeVvbg_bn-tnFM6o5qburXkdR1CEAruOiJzH8NORFIg8YgMqpGtdzNdhCxDsIJelIt3yUUmR9hkAEZK8.jpg -> ZagirAminev/allPhoto/iZQJZ4QSXeVvbg_bn-tnFM6o5qburXkdR1CEAruOiJzH8NORFIg8YgMqpGtdzNdhCxDsIJelIt3yUUmR9hkAEZK8.jpg
	renamed:    ZagirAminev/src/photo/koQkc-8N48CIK5Eyci_JTitKfy3Mj9WkTEv4755clOB_NCXKglQsIdIn__a3SNPFhvQapsayHRTUa0o8khpwordS.jpg -> ZagirAminev/allPhoto/koQkc-8N48CIK5Eyci_JTitKfy3Mj9WkTEv4755clOB_NCXKglQsIdIn__a3SNPFhvQapsayHRTUa0o8khpwordS.jpg
	renamed:    ZagirAminev/src/photo/mOZeq1KR1Umcyc4NO-YIilQRSBw7O--D-WYCfY0JBmmp7Zq2uPOVRMw64HR7-KXqCDiZIi8terx1AQXLhE-AZf9u.jpg -> ZagirAminev/allPhoto/mOZeq1KR1Umcyc4NO-YIilQRSBw7O--D-WYCfY0JBmmp7Zq2uPOVRMw64HR7-KXqCDiZIi8terx1AQXLhE-AZf9u.jpg
	renamed:    ZagirAminev/src/photo/rSi4RU0F0LG87Y3yy9ccRtJILTIqOYgmVSNTzxCqmO-1oHgTBs_Nak2x79gVOWUMI6BEOJbu6__t6rZ818fX3ms7.jpg -> ZagirAminev/allPhoto/rSi4RU0F0LG87Y3yy9ccRtJILTIqOYgmVSNTzxCqmO-1oHgTBs_Nak2x79gVOWUMI6BEOJbu6__t6rZ818fX3ms7.jpg
	renamed:    ZagirAminev/src/photo/t-bRlBHT178NfhzrUioK0mGtGW5onpiR_BBPd8imEDMZgHj_iyeV5wl9HN-ql_IlzQjnTz_YMb1ktJLHKctMQpuF.jpg -> ZagirAminev/allPhoto/t-bRlBHT178NfhzrUioK0mGtGW5onpiR_BBPd8imEDMZgHj_iyeV5wl9HN-ql_IlzQjnTz_YMb1ktJLHKctMQpuF.jpg
	modified:   serv_nginx/nginx/nginx-ssl.conf
moove photo
2025-08-08 23:47:32 +05:00
valitovgaziz 8d20ef7664 modified: serv_nginx/docker-compose.yml
modified:   serv_nginx/nginx/nginx-ssl.conf
change path to idnex.html begushiybashkir
2025-08-08 23:28:14 +05:00
valitovgaziz 69a0fb02f2 modified: serv_nginx/nginx/switch-config.sh
change domain to panycode begushiybashkir
2025-08-08 23:24:05 +05:00
valitovgaziz 4cb5b447a1 modified: serv_nginx/certbot/scripts/checkRenewCerts.sh
modified:   serv_nginx/certbot/scripts/init-certbot.sh
	modified:   serv_nginx/nginx/nginx-ssl.conf
change бегущийбашкир.рф domain in panycode
2025-08-08 23:21:48 +05:00
valitovgaziz 43cf07e1c6 modified: serv_nginx/.env
modified:   serv_nginx/certbot/Dockerfile
change domain name with Pubycode
2025-08-08 23:13:07 +05:00
valitovgaziz 627a035c77 modified: serv_nginx/nginx/nginx-http.conf
delete lisseners on 443 ssl prt
2025-08-08 22:34:46 +05:00
valitovgaziz b1979ce8a9 modified: serv_nginx/nginx/switch-config.sh
fix bag line 8
2025-08-08 22:32:40 +05:00
valitovgaziz 509435970d modified: serv_nginx/nginx/nginx-http.conf
add redirec from 443 to 80
2025-08-08 22:22:05 +05:00
valitovgaziz b4a57adf8e modified: serv_nginx/nginx/nginx-http.conf
fix one more bag
2025-08-08 22:08:25 +05:00
valitovgaziz 8ef9d2da1d modified: serv_nginx/nginx/nginx-http.conf
fix bag
2025-08-08 22:04:27 +05:00
valitovgaziz 3158158e72 modified: serv_nginx/nginx/nginx-http.conf
add https on 443 port stub site
2025-08-08 22:00:17 +05:00
valitovgaziz 97dd518bac modified: serv_nginx/docker-compose.yml
fix path for begushiyBashkir.rf
2025-08-08 21:57:12 +05:00
valitovgaziz b1c00072e8 modified: serv_nginx/nginx/switch-config.sh
update switch config nginx
2025-08-08 21:53:24 +05:00
valitovgaziz 4c1aba0106 modified: serv_nginx/.env
modified:   serv_nginx/certbot/config/certbot.ini
	modified:   serv_nginx/certbot/scripts/checkRenewCerts.sh
	modified:   serv_nginx/certbot/scripts/init-certbot.sh
	new file:   serv_nginx/certbot/scripts/renewBegushiyBashkir.sh
	modified:   serv_nginx/certbot/scripts/renewEasysite102.sh
	modified:   serv_nginx/certbot/scripts/renewValitovGazizCert.sh
	modified:   serv_nginx/certbot/scripts/renewYalarbaCert.sh
prettify cod
2025-08-08 20:56:31 +05:00
valitovgaziz 05af602048 modified: README.md
modified:   serv_nginx/docker-compose.yml
	modified:   serv_nginx/nginx/nginx-http.conf
	modified:   serv_nginx/stubSite/index.html
add stub site
2025-08-08 20:43:44 +05:00
valitovgaziz f565cf1ffc modified: serv_nginx/docker-compose.yml
modified:   serv_nginx/nginx/Dockerfile
	modified:   serv_nginx/nginx/nginx-http.conf
	modified:   serv_nginx/nginx/nginx-ssl.conf
	modified:   serv_nginx/nginx/switch-config.sh
	new file:   serv_nginx/stubSite/index.html
add stub site and add бегущийбашкир.рф domain
2025-08-08 20:29:05 +05:00
Gaziz Valitov 4b88b00a3a изменено: README.md
add easysite102.ru with merge cerbot and nginx services
2025-08-08 18:06:22 +05:00
valitovgaziz 866a1150da new file: easySite/easy-site/prod/index.html
modified:   serv_nginx/docker-compose.yml
add stub site and change path to site
2025-08-08 17:53:49 +05:00
valitovgaziz aefc5bb7fa modified: serv_nginx/docker-compose.yml
change path to easysite
2025-08-08 17:49:03 +05:00
valitovgaziz ce11b30900 modified: serv_nginx/certbot/scripts/init-certbot.sh
use full path
2025-08-08 17:44:36 +05:00
valitovgaziz eb3cdacd9e modified: serv_nginx/certbot/scripts/init-certbot.sh
change command for start crone service into file
2025-08-08 17:40:10 +05:00
valitovgaziz ad88d2db13 modified: serv_nginx/certbot/Dockerfile
check crone
2025-08-08 17:36:06 +05:00
valitovgaziz bc6a0d479a modified: serv_nginx/certbot/Dockerfile
Change checking crone for sh
2025-08-08 17:32:53 +05:00
valitovgaziz d5b945cc7d modified: serv_nginx/certbot/Dockerfile
add check for cron in conteiner
2025-08-08 17:30:26 +05:00
valitovgaziz 195a532f0b modified: serv_nginx/certbot/scripts/init-certbot.sh
Add logs into file
2025-08-08 17:26:30 +05:00
valitovgaziz 5adbb7d694 modified: serv_nginx/docker-compose.yml
new file:   valitovgaziz/.env
change paths for mounting sites
2025-08-08 17:20:33 +05:00
valitovgaziz 9457072fa0 modified: nginx/switch-config.sh
add checking for all domains
2025-08-08 17:13:41 +05:00
valitovgaziz 0a5da73848 modified: certbot/Dockerfile
merge conflict
2025-08-08 16:59:10 +05:00
valitovgaziz 5fabc2694b modified: docker-compose.yml
depends_on midifaed
2025-08-08 16:56:59 +05:00
valitovgaziz 73114b8506 deleted: serv_certbot/.env
deleted:    serv_certbot/docker-compose.yml
	modified:   serv_nginx/.env
	renamed:    serv_certbot/certbot/Dockerfile -> serv_nginx/certbot/Dockerfile
	renamed:    serv_certbot/certbot/config/certbot.ini -> serv_nginx/certbot/config/certbot.ini
renamed:    serv_certbot/certbot/scripts/checkRenewCerts.sh -> serv_nginx/certbot/scripts/checkRenewCerts.sh
	renamed:    serv_certbot/certbot/scripts/crontab.txt -> serv_nginx/certbot/scripts/crontab.txt
	renamed:    serv_certbot/certbot/scripts/init-certbot.sh -> serv_nginx/certbot/scripts/init-certbot.sh
	renamed:    serv_certbot/certbot/scripts/renewEasysite102.sh -> serv_nginx/certbot/scripts/renewEasysite102.sh
	renamed:    serv_certbot/certbot/scripts/renewValitovGazizCert.sh -> serv_nginx/certbot/scripts/renewValitovGazizCert.sh
	renamed:    serv_certbot/certbot/scripts/renewYalarbaCert.sh -> serv_nginx/certbot/scripts/renewYalarbaCert.sh
	modified:   serv_nginx/docker-compose.yml
merge certbot and nginx services
2025-08-08 16:54:47 +05:00
valitovgaziz 290cf1fe14 Merge branch 'main' of https://github.com/valitovgaziz/tp 2025-08-08 16:52:42 +05:00
valitovgaziz d694795ed7 modified: .gitignore
new file:   serv_certbot/.env
	new file:   serv_migration/.env
	new file:   serv_nginx/.env
	new file:   serv_rest_api/.env
	new file:   serv_spa/.env
add .env files into all servs
2025-08-08 16:52:11 +05:00
valitovgaziz 3de692cf8b modified: .env
add .env into serv_nginx
2025-08-08 16:44:26 +05:00
valitovgaziz 50f778a831 Merge branch 'main' of github.com:valitovgaziz/tp 2025-08-08 16:43:36 +05:00
valitovgaziz ddc1fb1dbb modified: serv_nginx/docker-compose.yml
remove certbot dependency
2025-08-08 16:16:20 +05:00
valitovgaziz 0e9730a55c Merge branch 'divite_services'
modified:   .env
	modified:   .gitignore
	modified:   Makefile
	modified:   README.md
	deleted:    api/Makefile
	deleted:    certbot/Dockerfile
	deleted:    certbot/scripts/init-certbot.sh
	modified:   docker-compose.yaml
	new file:   easySite/easy-site/README.md
	new file:   easySite/easy-site/build/asset-manifest.json
	new file:   easySite/easy-site/build/index.html
	new file:   easySite/easy-site/build/static/js/main.9a8f7abd.js
	new file:   easySite/easy-site/build/static/js/main.9a8f7abd.js.LICENSE.txt
	new file:   easySite/easy-site/build/static/js/main.9a8f7abd.js.map
	new file:   easySite/easy-site/package-lock.json
	new file:   easySite/easy-site/package.json
	new file:   easySite/easy-site/postcss.config.js
	new file:   easySite/easy-site/public/index.html
	new file:   easySite/easy-site/src/App.css
	new file:   easySite/easy-site/src/App.js
	new file:   easySite/easy-site/src/App.test.js
	new file:   easySite/easy-site/src/contexts/AuthContext.js
	new file:   easySite/easy-site/src/contexts/useAuth.js
	new file:   easySite/easy-site/src/index.css
	new file:   easySite/easy-site/src/index.js
	new file:   easySite/easy-site/src/logo.svg
	new file:   easySite/easy-site/src/pages/AddObject.js
	new file:   easySite/easy-site/src/pages/Dashboard.js
	new file:   easySite/easy-site/src/pages/EditObject.js
	new file:   easySite/easy-site/src/pages/Home.js
	new file:   easySite/easy-site/src/pages/Login.js
	new file:   easySite/easy-site/src/pages/Register.js
	new file:   easySite/easy-site/src/reportWebVitals.js
	new file:   easySite/easy-site/src/setupTests.js
	new file:   easySite/easy-site/tailwind.config.js
	deleted:    nginx/nginx-ssl.conf
	new file:   node_servers/authserver.js
	new file:   node_servers/feedbackserver.js
	new file:   node_servers/techsupportserver.js
	new file:   serv_certbot/certbot/Dockerfile
	renamed:    certbot/config/certbot.ini -> serv_certbot/certbot/config/certbot.ini
	renamed:    certbot/scripts/checkRenewCerts.sh -> serv_certbot/certbot/scripts/checkRenewCerts.sh
	new file:   serv_certbot/certbot/scripts/crontab.txt
	new file:   serv_certbot/certbot/scripts/init-certbot.sh
	new file:   serv_certbot/certbot/scripts/renewEasysite102.sh
	renamed:    certbot/scripts/renewValitovGazizCert.sh -> serv_certbot/certbot/scripts/renewValitovGazizCert.sh
	renamed:    certbot/scripts/renewYalarbaCert.sh -> serv_certbot/certbot/scripts/renewYalarbaCert.sh
	new file:   serv_certbot/docker-compose.yml
	new file:   serv_migration/docker-compose.yml
	renamed:    migrator/Dockerfile -> serv_migration/migrator/Dockerfile
	renamed:    migrator/go.mod -> serv_migration/migrator/go.mod
	renamed:    migrator/go.sum -> serv_migration/migrator/go.sum
	renamed:    migrator/migrations/20240819162009_create_users_table.sql -> serv_migration/migrator/migrations/20240819162009_create_users_table.sql
	renamed:    migrator/migrations/20240825004755_add_column_role_to_users.sql -> serv_migration/migrator/migrations/20240825004755_add_column_role_to_users.sql
	new file:   serv_nginx/docker-compose.yml
	renamed:    nginx/Dockerfile -> serv_nginx/nginx/Dockerfile
	renamed:    nginx/nginx-http.conf -> serv_nginx/nginx/nginx-http.conf
	new file:   serv_nginx/nginx/nginx-ssl.conf
	renamed:    nginx/switch-config.sh -> serv_nginx/nginx/switch-config.sh
	renamed:    api/Dockerfile -> serv_rest_api/api/Dockerfile
	renamed:    api/bin/api.exe -> serv_rest_api/api/bin/api.exe
	renamed:    api/cmd/main.go -> serv_rest_api/api/cmd/main.go
	renamed:    api/go.mod -> serv_rest_api/api/go.mod
	renamed:    api/go.sum -> serv_rest_api/api/go.sum
	renamed:    api/src/configs/APIserver.go -> serv_rest_api/api/src/configs/APIserver.go
	renamed:    api/src/configs/DBconfig.go -> serv_rest_api/api/src/configs/DBconfig.go
	renamed:    api/src/initializers/PGQL_DB.go -> serv_rest_api/api/src/initializers/PGQL_DB.go
	renamed:    api/src/initializers/Routing.go -> serv_rest_api/api/src/initializers/Routing.go
	renamed:    api/src/models/Contacts.go -> serv_rest_api/api/src/models/Contacts.go
	renamed:    api/src/models/Essence.go -> serv_rest_api/api/src/models/Essence.go
	renamed:    api/src/models/Point.go -> serv_rest_api/api/src/models/Point.go
	renamed:    api/src/models/authDataStructs.go -> serv_rest_api/api/src/models/authDataStructs.go
	renamed:    api/src/models/user.go -> serv_rest_api/api/src/models/user.go
	renamed:    api/src/rt/admin/users.go -> serv_rest_api/api/src/rt/admin/users.go
	renamed:    api/src/rt/auth/Login.go -> serv_rest_api/api/src/rt/auth/Login.go
	renamed:    api/src/rt/auth/Registr.go -> serv_rest_api/api/src/rt/auth/Registr.go
	renamed:    api/src/rt/auth/authAdminMiddlware.go -> serv_rest_api/api/src/rt/auth/authAdminMiddlware.go
	renamed:    api/src/rt/auth/authMiddleware.go -> serv_rest_api/api/src/rt/auth/authMiddleware.go
	renamed:    api/src/rt/auth/auth_test.go -> serv_rest_api/api/src/rt/auth/auth_test.go
	renamed:    api/src/rt/prf/AuthUserMiddleWare.go -> serv_rest_api/api/src/rt/prf/AuthUserMiddleWare.go
	renamed:    api/src/rt/prf/profile.go -> serv_rest_api/api/src/rt/prf/profile.go
	renamed:    api/src/rt/srch/search.go -> serv_rest_api/api/src/rt/srch/search.go
	renamed:    api/src/storages/psql/psql.go -> serv_rest_api/api/src/storages/psql/psql.go
	new file:   serv_rest_api/docker-compose.yml
	new file:   serv_spa/docker-compose.yml
	new file:   serv_spa/spa/Dockerfile
	new file:   serv_spa/spa/app/assets/bage_logo.png
	new file:   serv_spa/spa/app/index.html
	new file:   serv_spa/spa/app/styles/mainStyle.css
	new file:   serv_spa/spa/vue/README.md
	new file:   serv_spa/spa/vue/advices.txt
2025-08-08 16:12:54 +05:00
valitovgaziz 1e83239dcc new file: serv_rest_api/api/Makefile
new file:   serv_spa/spa/vue/Makefile
divide spa and rest api
2025-08-08 16:09:52 +05:00
valitovgaziz c1a8497e60 Merge branch 'main' of github.com:valitovgaziz/tp 2025-08-08 15:00:18 +05:00
Gaziz Valitov a6dc7e181d новый файл: ZagirAminev/src/assets/bashkorsa.md
новый файл:    ZagirAminev/src/index.html
	новый файл:    ZagirAminev/src/photo/-IpB6SO4w3czu57WpHRILQecfpnfYl--XAi0dLsktbPDg0HRthR1yX_xrc6yIJ4fs0vsXpxnBl40K_7MArdCHv5e.jpg
	новый файл:    ZagirAminev/src/photo/34xV0jF7No0LV-4tMT4V8YVp9AhGNqramMwxKR5TzjR857GuSiSHghxPQgg0UXg7HPgwSQ5weoZuCAiYus7XCaSM.jpg
	новый файл:    ZagirAminev/src/photo/50geYH7D5Sdv0JyVsCzwXOwf9S0AZY-VfEz2dkUjIC8iqf0i90_1TWpbkAalCkmk_NzTAD978-m9dK4481sH1Bno.jpg
	новый файл:    ZagirAminev/src/photo/7R3tXacvNpZfpjn-3M-Zq78kweKe5t_bUUCLynm7b9iwKNPZqyqFQhevjqitDn-Q8GRpGJZSwecrn-Gg2AcaKfnK.jpg
	новый файл:    ZagirAminev/src/photo/AtylqrEsbMCdt79zyh8qtA4Z9zeBM9kpp6T7NGukNUnm1aYHA6D52sQNmRzTE18LIWiBwdk9oZllNZWihCPZJFXG.jpg
	новый файл:    ZagirAminev/src/photo/F8Tn7pi0n9vgC5p1xtZuVJB9WTFdgMqCa6d4Pcx1LvCL9_SP6YMOiNx3jfPu5365h7C4JIvklQ6DFQ7o9pWI6n1y.jpg
	новый файл:    ZagirAminev/src/photo/IuIO58XcgyITp7u-nxv98_giL9RAPqA0Ou64Hv-RSccSBqVaJ4tF8ttI-XnMb57IdxWZOYAr-tfOr2G8-dDksxOT.jpg
	новый файл:    ZagirAminev/src/photo/JVKp07ubApAxESkcLN7Y44SCpclHDAJAre-bpEMeZfqW5h4Jvaw-czUSC8CpR5EbZzYtaVkE-vEuSx15BvwG2Ain.jpg
	новый файл:    ZagirAminev/src/photo/MBt1Fi50c-0swicDcompcDMau69AtnpOHf4ww9M9jrULUtTSaMeadyecnmqzw8Xsmpon3m2mHjzYK3VCXKCsOcL2.jpg
	новый файл:    ZagirAminev/src/photo/PTILTFDmM9l6UBXoEHrwMUQeq3F61oKqTUVr1nPqMVNg3XjxjC2t463cRmg9UxH_R31U_vgdebAE2xkkbG7nTTK4.jpg
	новый файл:    ZagirAminev/src/photo/RunnerHome.jpg
	новый файл:    ZagirAminev/src/photo/VuvtXAIwJk9KkfKfOT6MaJ9D-qJ4TlPfC7R_oeKbMGMoaIko5xj3Jm_X__kuvttLvCitCBmV5jR6kXXPolrGEl_N.jpg
	новый файл:    ZagirAminev/src/photo/ZagirTrainer.jpg
	новый файл:    ZagirAminev/src/photo/ZagirTrainer2.jpg
	новый файл:    ZagirAminev/src/photo/_HPWM2Ok1DyxksDiHkOtNQdgCqkO8wWdLaK2RBPNbu6liCUwiiivqwtR6P1y12ATUbElNVHkVjiM94ZzOiH49maO.jpg
	новый файл:    ZagirAminev/src/photo/bwnopBq78b6r0HeJK2FLskwlk2soeqV8AC-ve10-WH8XX8yy2aD5d8pc57pJnu8BcdZT8xneCGl7FpPTiCaGePQn.jpg
	новый файл:    ZagirAminev/src/photo/eMsY_q4QXF58mRumDerwjaj82MY37q2PBdkZaLIZegcl18HgzifPvjaD0hIyPbNDOde2IZZNnV79ydbeYiCmVIPi.jpg
	новый файл:    ZagirAminev/src/photo/eo3EVEbCACsiPNXupOsaIyVOTVLbCqWSm68NSutgeccGBdgOSbctQ0Gzr2I-fMjFsU2KADbVHQ3bwk_j6S7HAygl.jpg
	новый файл:    ZagirAminev/src/photo/iZQJZ4QSXeVvbg_bn-tnFM6o5qburXkdR1CEAruOiJzH8NORFIg8YgMqpGtdzNdhCxDsIJelIt3yUUmR9hkAEZK8.jpg
	новый файл:    ZagirAminev/src/photo/koQkc-8N48CIK5Eyci_JTitKfy3Mj9WkTEv4755clOB_NCXKglQsIdIn__a3SNPFhvQapsayHRTUa0o8khpwordS.jpg
	новый файл:    ZagirAminev/src/photo/mOZeq1KR1Umcyc4NO-YIilQRSBw7O--D-WYCfY0JBmmp7Zq2uPOVRMw64HR7-KXqCDiZIi8terx1AQXLhE-AZf9u.jpg
	новый файл:    ZagirAminev/src/photo/rSi4RU0F0LG87Y3yy9ccRtJILTIqOYgmVSNTzxCqmO-1oHgTBs_Nak2x79gVOWUMI6BEOJbu6__t6rZ818fX3ms7.jpg
	новый файл:    ZagirAminev/src/photo/t-bRlBHT178NfhzrUioK0mGtGW5onpiR_BBPd8imEDMZgHj_iyeV5wl9HN-ql_IlzQjnTz_YMb1ktJLHKctMQpuF.jpg
	новый файл:    ZagirAminev/src/script.js
	новый файл:    ZagirAminev/src/styles/descktopStyle.css
	новый файл:    ZagirAminev/src/styles/mobileStyle.css
	новый файл:    ZagirAminev/src/styles/style.css
	новый файл:    ZagirAminev/src/styles/tabletStyle.css
2025-08-08 14:58:50 +05:00
valitovgaziz f379f2e6c5 modified: README.md
modified:   docker-compose.yaml
	new file:   serv_nginx/docker-compose.yml
	renamed:    nginx/Dockerfile -> serv_nginx/nginx/Dockerfile
	renamed:    nginx/nginx-http.conf -> serv_nginx/nginx/nginx-http.conf
	renamed:    nginx/nginx-ssl.conf -> serv_nginx/nginx/nginx-ssl.conf
	renamed:    nginx/switch-config.sh -> serv_nginx/nginx/switch-config.sh
divide nginx service
2025-08-01 05:04:59 +05:00
valitovgaziz d06236556d modified: README.md
modified:   docker-compose.yaml
	renamed:    certbot/Dockerfile -> serv_certbot/certbot/Dockerfile
	renamed:    certbot/config/certbot.ini -> serv_certbot/certbot/config/certbot.ini
	renamed:    certbot/scripts/checkRenewCerts.sh -> serv_certbot/certbot/scripts/checkRenewCerts.sh
	renamed:    certbot/scripts/crontab.txt -> serv_certbot/certbot/scripts/crontab.txt
	renamed:    certbot/scripts/init-certbot.sh -> serv_certbot/certbot/scripts/init-certbot.sh
	renamed:    certbot/scripts/renewEasysite102.sh -> serv_certbot/certbot/scripts/renewEasysite102.sh
	renamed:    certbot/scripts/renewValitovGazizCert.sh -> serv_certbot/certbot/scripts/renewValitovGazizCert.sh
	renamed:    certbot/scripts/renewYalarbaCert.sh -> serv_certbot/certbot/scripts/renewYalarbaCert.sh
	new file:   serv_certbot/docker-compose.yml
divite certbot service
2025-07-31 10:31:17 +05:00
valitovgaziz 00fb60f8d4 deleted: package-lock.json
deleted:    package.json
delete files
2025-07-31 10:14:50 +05:00
valitovgaziz e1a6728042 modified: .env
modified:   README.md
	modified:   docker-compose.yaml
	renamed:    rest_api/api/Dockerfile -> serv_rest_api/api/Dockerfile
	renamed:    rest_api/api/bin/api.exe -> serv_rest_api/api/bin/api.exe
	renamed:    rest_api/api/cmd/main.go -> serv_rest_api/api/cmd/main.go
	renamed:    rest_api/api/go.mod -> serv_rest_api/api/go.mod
	renamed:    rest_api/api/go.sum -> serv_rest_api/api/go.sum
	renamed:    rest_api/api/src/configs/APIserver.go -> serv_rest_api/api/src/configs/APIserver.go
	renamed:    rest_api/api/src/configs/DBconfig.go -> serv_rest_api/api/src/configs/DBconfig.go
	renamed:    rest_api/api/src/initializers/PGQL_DB.go -> serv_rest_api/api/src/initializers/PGQL_DB.go
	renamed:    rest_api/api/src/initializers/Routing.go -> serv_rest_api/api/src/initializers/Routing.go
	renamed:    rest_api/api/src/models/Contacts.go -> serv_rest_api/api/src/models/Contacts.go
	renamed:    rest_api/api/src/models/Essence.go -> serv_rest_api/api/src/models/Essence.go
	renamed:    rest_api/api/src/models/Point.go -> serv_rest_api/api/src/models/Point.go
	renamed:    rest_api/api/src/models/authDataStructs.go -> serv_rest_api/api/src/models/authDataStructs.go
	renamed:    rest_api/api/src/models/user.go -> serv_rest_api/api/src/models/user.go
	renamed:    rest_api/api/src/rt/admin/users.go -> serv_rest_api/api/src/rt/admin/users.go
	renamed:    rest_api/api/src/rt/auth/Login.go -> serv_rest_api/api/src/rt/auth/Login.go
	renamed:    rest_api/api/src/rt/auth/Registr.go -> serv_rest_api/api/src/rt/auth/Registr.go
	renamed:    rest_api/api/src/rt/auth/authAdminMiddlware.go -> serv_rest_api/api/src/rt/auth/authAdminMiddlware.go
	renamed:    rest_api/api/src/rt/auth/authMiddleware.go -> serv_rest_api/api/src/rt/auth/authMiddleware.go
	renamed:    rest_api/api/src/rt/auth/auth_test.go -> serv_rest_api/api/src/rt/auth/auth_test.go
	renamed:    rest_api/api/src/rt/prf/AuthUserMiddleWare.go -> serv_rest_api/api/src/rt/prf/AuthUserMiddleWare.go
	renamed:    rest_api/api/src/rt/prf/profile.go -> serv_rest_api/api/src/rt/prf/profile.go
	renamed:    rest_api/api/src/rt/srch/search.go -> serv_rest_api/api/src/rt/srch/search.go
	renamed:    rest_api/api/src/storages/psql/psql.go -> serv_rest_api/api/src/storages/psql/psql.go
	renamed:    rest_api/docker-compose.yml -> serv_rest_api/docker-compose.yml
	new file:   serv_spa/docker-compose.yml
	renamed:    spa/Dockerfile -> serv_spa/spa/Dockerfile
	renamed:    spa/app/assets/bage_logo.png -> serv_spa/spa/app/assets/bage_logo.png
	renamed:    spa/app/index.html -> serv_spa/spa/app/index.html
	renamed:    spa/app/styles/mainStyle.css -> serv_spa/spa/app/styles/mainStyle.css
	renamed:    spa/vue/README.md -> serv_spa/spa/vue/README.md
	renamed:    spa/vue/advices.txt -> serv_spa/spa/vue/advices.txt
	renamed:    spa/vue/dist/assets/AboutView-B7yELX4Y.js -> serv_spa/spa/vue/dist/assets/AboutView-B7yELX4Y.js
	renamed:    spa/vue/dist/assets/AboutView-C1FaiPPz.css -> serv_spa/spa/vue/dist/assets/AboutView-C1FaiPPz.css
	renamed:    spa/vue/dist/assets/FeetbackView-CXGcB7N7.js -> serv_spa/spa/vue/dist/assets/FeetbackView-CXGcB7N7.js
	renamed:    spa/vue/dist/assets/FeetbackView-Csl9V8Bc.css -> serv_spa/spa/vue/dist/assets/FeetbackView-Csl9V8Bc.css
	renamed:    spa/vue/dist/assets/HomeView-C0JCTEr_.css -> serv_spa/spa/vue/dist/assets/HomeView-C0JCTEr_.css
	renamed:    spa/vue/dist/assets/HomeView-zKlroz3N.js -> serv_spa/spa/vue/dist/assets/HomeView-zKlroz3N.js
	renamed:    spa/vue/dist/assets/LogInView-CCzg48Go.js -> serv_spa/spa/vue/dist/assets/LogInView-CCzg48Go.js
	renamed:    spa/vue/dist/assets/LogInView-Co-3gSCv.css -> serv_spa/spa/vue/dist/assets/LogInView-Co-3gSCv.css
	renamed:    spa/vue/dist/assets/OpenSans-Italic-Cv5d4RQ-.woff -> serv_spa/spa/vue/dist/assets/OpenSans-Italic-Cv5d4RQ-.woff
	renamed:    spa/vue/dist/assets/OpenSans-Italic-DNMplG0v.woff2 -> serv_spa/spa/vue/dist/assets/OpenSans-Italic-DNMplG0v.woff2
	renamed:    spa/vue/dist/assets/OpenSans-Regular-BT0WUJf-.woff2 -> serv_spa/spa/vue/dist/assets/OpenSans-Regular-BT0WUJf-.woff2
	renamed:    spa/vue/dist/assets/OpenSans-Regular-C74AleX8.woff -> serv_spa/spa/vue/dist/assets/OpenSans-Regular-C74AleX8.woff
	renamed:    spa/vue/dist/assets/ProfileView-BuVzjMSP.js -> serv_spa/spa/vue/dist/assets/ProfileView-BuVzjMSP.js
	renamed:    spa/vue/dist/assets/ProfileView-COXJamv9.css -> serv_spa/spa/vue/dist/assets/ProfileView-COXJamv9.css
	renamed:    spa/vue/dist/assets/RegistrationView-C0Di_tAT.js -> serv_spa/spa/vue/dist/assets/RegistrationView-C0Di_tAT.js
	renamed:    spa/vue/dist/assets/RegistrationView-D_FgJHPY.css -> serv_spa/spa/vue/dist/assets/RegistrationView-D_FgJHPY.css
	renamed:    spa/vue/dist/assets/RestObjectView-CuQAHRrm.js -> serv_spa/spa/vue/dist/assets/RestObjectView-CuQAHRrm.js
	renamed:    spa/vue/dist/assets/RestObjectView-DU_QDLXy.css -> serv_spa/spa/vue/dist/assets/RestObjectView-DU_QDLXy.css
	renamed:    spa/vue/dist/assets/ResultsView-C1CSY9zD.js -> serv_spa/spa/vue/dist/assets/ResultsView-C1CSY9zD.js
	renamed:    spa/vue/dist/assets/ResultsView-ijT9kjlC.css -> serv_spa/spa/vue/dist/assets/ResultsView-ijT9kjlC.css
	renamed:    spa/vue/dist/assets/Roboto-Italic-DS1crIJz.woff2 -> serv_spa/spa/vue/dist/assets/Roboto-Italic-DS1crIJz.woff2
	renamed:    spa/vue/dist/assets/Roboto-Italic-uZr_9iaA.woff -> serv_spa/spa/vue/dist/assets/Roboto-Italic-uZr_9iaA.woff
	renamed:    spa/vue/dist/assets/Roboto-Regular-jtJqKL8A.woff2 -> serv_spa/spa/vue/dist/assets/Roboto-Regular-jtJqKL8A.woff2
	renamed:    spa/vue/dist/assets/Roboto-Regular-n8vMMKqC.woff -> serv_spa/spa/vue/dist/assets/Roboto-Regular-n8vMMKqC.woff
	renamed:    spa/vue/dist/assets/SettingsView-BlE1G-Ym.js -> serv_spa/spa/vue/dist/assets/SettingsView-BlE1G-Ym.js
	renamed:    spa/vue/dist/assets/SettingsView-FlTPuBSa.css -> serv_spa/spa/vue/dist/assets/SettingsView-FlTPuBSa.css
	renamed:    spa/vue/dist/assets/SupportView-B_EO3gSX.js -> serv_spa/spa/vue/dist/assets/SupportView-B_EO3gSX.js
	renamed:    spa/vue/dist/assets/SupportView-DF9LsVNx.css -> serv_spa/spa/vue/dist/assets/SupportView-DF9LsVNx.css
	renamed:    spa/vue/dist/assets/YalArbaLogo300-BGWtRO6S.png -> serv_spa/spa/vue/dist/assets/YalArbaLogo300-BGWtRO6S.png
	renamed:    spa/vue/dist/assets/footerB-DAi6c01a.js -> serv_spa/spa/vue/dist/assets/footerB-DAi6c01a.js
	renamed:    spa/vue/dist/assets/footerB-D_GT2umv.css -> serv_spa/spa/vue/dist/assets/footerB-D_GT2umv.css
	renamed:    spa/vue/dist/assets/hinted-OpenSans-Italic-BD2qe0ib.ttf -> serv_spa/spa/vue/dist/assets/hinted-OpenSans-Italic-BD2qe0ib.ttf
	renamed:    spa/vue/dist/assets/hinted-OpenSans-Regular-DZwYhniE.ttf -> serv_spa/spa/vue/dist/assets/hinted-OpenSans-Regular-DZwYhniE.ttf
	renamed:    spa/vue/dist/assets/hinted-Roboto-Italic-CDlkg4IH.ttf -> serv_spa/spa/vue/dist/assets/hinted-Roboto-Italic-CDlkg4IH.ttf
	renamed:    spa/vue/dist/assets/hinted-Roboto-Regular-CFXqJ5DR.ttf -> serv_spa/spa/vue/dist/assets/hinted-Roboto-Regular-CFXqJ5DR.ttf
	renamed:    spa/vue/dist/assets/index-B44ZMC8Y.css -> serv_spa/spa/vue/dist/assets/index-B44ZMC8Y.css
	renamed:    spa/vue/dist/assets/index-DccGIixi.js -> serv_spa/spa/vue/dist/assets/index-DccGIixi.js
	renamed:    spa/vue/dist/assets/logo150x150-De-v7fGb.png -> serv_spa/spa/vue/dist/assets/logo150x150-De-v7fGb.png
	renamed:    spa/vue/dist/assets/photo_2025-01-25_05-57-24-BwgIchq6.jpg -> serv_spa/spa/vue/dist/assets/photo_2025-01-25_05-57-24-BwgIchq6.jpg
	renamed:    spa/vue/dist/assets/restObject-CcS1cBol.jpg -> serv_spa/spa/vue/dist/assets/restObject-CcS1cBol.jpg
	renamed:    spa/vue/dist/assets/searchLine-3vDx-8Ye.js -> serv_spa/spa/vue/dist/assets/searchLine-3vDx-8Ye.js
	renamed:    spa/vue/dist/assets/searchLine-Bqe_ccJt.css -> serv_spa/spa/vue/dist/assets/searchLine-Bqe_ccJt.css
	renamed:    spa/vue/dist/index.html -> serv_spa/spa/vue/dist/index.html
	renamed:    spa/vue/index.html -> serv_spa/spa/vue/index.html
	renamed:    spa/vue/jsconfig.json -> serv_spa/spa/vue/jsconfig.json
	renamed:    spa/vue/package-lock.json -> serv_spa/spa/vue/package-lock.json
	renamed:    spa/vue/package.json -> serv_spa/spa/vue/package.json
	renamed:    spa/vue/src/App.vue -> serv_spa/spa/vue/src/App.vue
	renamed:    spa/vue/src/assets/colors.css -> serv_spa/spa/vue/src/assets/colors.css
	renamed:    spa/vue/src/assets/fonts.css -> serv_spa/spa/vue/src/assets/fonts.css
	renamed:    spa/vue/src/assets/fonts/Open_Sans/OFL.txt -> serv_spa/spa/vue/src/assets/fonts/Open_Sans/OFL.txt
	renamed:    spa/vue/src/assets/fonts/Open_Sans/OpenSans-Italic-VariableFont_wdth,wght.ttf -> serv_spa/spa/vue/src/assets/fonts/Open_Sans/OpenSans-Italic-VariableFont_wdth,wght.ttf
	renamed:    spa/vue/src/assets/fonts/Open_Sans/OpenSans-Italic.woff -> serv_spa/spa/vue/src/assets/fonts/Open_Sans/OpenSans-Italic.woff
	renamed:    spa/vue/src/assets/fonts/Open_Sans/OpenSans-Italic.woff2 -> serv_spa/spa/vue/src/assets/fonts/Open_Sans/OpenSans-Italic.woff2
	renamed:    spa/vue/src/assets/fonts/Open_Sans/OpenSans-Regular.woff -> serv_spa/spa/vue/src/assets/fonts/Open_Sans/OpenSans-Regular.woff
	renamed:    spa/vue/src/assets/fonts/Open_Sans/OpenSans-Regular.woff2 -> serv_spa/spa/vue/src/assets/fonts/Open_Sans/OpenSans-Regular.woff2
	renamed:    spa/vue/src/assets/fonts/Open_Sans/OpenSans-VariableFont_wdth,wght.ttf -> serv_spa/spa/vue/src/assets/fonts/Open_Sans/OpenSans-VariableFont_wdth,wght.ttf
	renamed:    spa/vue/src/assets/fonts/Open_Sans/README.txt -> serv_spa/spa/vue/src/assets/fonts/Open_Sans/README.txt
	renamed:    spa/vue/src/assets/fonts/Open_Sans/hinted-OpenSans-Italic.eot -> serv_spa/spa/vue/src/assets/fonts/Open_Sans/hinted-OpenSans-Italic.eot
	renamed:    spa/vue/src/assets/fonts/Open_Sans/hinted-OpenSans-Italic.svg -> serv_spa/spa/vue/src/assets/fonts/Open_Sans/hinted-OpenSans-Italic.svg
	renamed:    spa/vue/src/assets/fonts/Open_Sans/hinted-OpenSans-Italic.ttf -> serv_spa/spa/vue/src/assets/fonts/Open_Sans/hinted-OpenSans-Italic.ttf
	renamed:    spa/vue/src/assets/fonts/Open_Sans/hinted-OpenSans-Regular.eot -> serv_spa/spa/vue/src/assets/fonts/Open_Sans/hinted-OpenSans-Regular.eot
	renamed:    spa/vue/src/assets/fonts/Open_Sans/hinted-OpenSans-Regular.svg -> serv_spa/spa/vue/src/assets/fonts/Open_Sans/hinted-OpenSans-Regular.svg
	renamed:    spa/vue/src/assets/fonts/Open_Sans/hinted-OpenSans-Regular.ttf -> serv_spa/spa/vue/src/assets/fonts/Open_Sans/hinted-OpenSans-Regular.ttf
	renamed:    spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-Bold.ttf -> serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-Bold.ttf
	renamed:    spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-BoldItalic.ttf -> serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-BoldItalic.ttf
	renamed:    spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-ExtraBold.ttf -> serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-ExtraBold.ttf
	renamed:    spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-ExtraBoldItalic.ttf -> serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-ExtraBoldItalic.ttf
	renamed:    spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-Italic.ttf -> serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-Italic.ttf
	renamed:    spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-Light.ttf -> serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-Light.ttf
	renamed:    spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-LightItalic.ttf -> serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-LightItalic.ttf
	renamed:    spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-Medium.ttf -> serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-Medium.ttf
	renamed:    spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-MediumItalic.ttf -> serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-MediumItalic.ttf
	renamed:    spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-Regular.ttf -> serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-Regular.ttf
	renamed:    spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-SemiBold.ttf -> serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-SemiBold.ttf
	renamed:    spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-SemiBoldItalic.ttf -> serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-SemiBoldItalic.ttf
	renamed:    spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-Bold.ttf -> serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-Bold.ttf
	renamed:    spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-BoldItalic.ttf -> serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-BoldItalic.ttf
	renamed:    spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-ExtraBold.ttf -> serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-ExtraBold.ttf
	renamed:    spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-ExtraBoldItalic.ttf -> serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-ExtraBoldItalic.ttf
	renamed:    spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-Italic.ttf -> serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-Italic.ttf
	renamed:    spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-Light.ttf -> serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-Light.ttf
	renamed:    spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-LightItalic.ttf -> serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-LightItalic.ttf
	renamed:    spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-Medium.ttf -> serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-Medium.ttf
	renamed:    spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-MediumItalic.ttf -> serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-MediumItalic.ttf
	renamed:    spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-Regular.ttf -> serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-Regular.ttf
	renamed:    spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-SemiBold.ttf -> serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-SemiBold.ttf
	renamed:    spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-SemiBoldItalic.ttf -> serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-SemiBoldItalic.ttf
	renamed:    spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-Bold.ttf -> serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-Bold.ttf
	renamed:    spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-BoldItalic.ttf -> serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-BoldItalic.ttf
	renamed:    spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-ExtraBold.ttf -> serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-ExtraBold.ttf
	renamed:    spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-ExtraBoldItalic.ttf -> serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-ExtraBoldItalic.ttf
	renamed:    spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-Italic.ttf -> serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-Italic.ttf
	renamed:    spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-Light.ttf -> serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-Light.ttf
	renamed:    spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-LightItalic.ttf -> serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-LightItalic.ttf
	renamed:    spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-Medium.ttf -> serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-Medium.ttf
	renamed:    spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-MediumItalic.ttf -> serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-MediumItalic.ttf
	renamed:    spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-Regular.ttf -> serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-Regular.ttf
	renamed:    spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-SemiBold.ttf -> serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-SemiBold.ttf
	renamed:    spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-SemiBoldItalic.ttf -> serv_spa/spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-SemiBoldItalic.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/OFL.txt -> serv_spa/spa/vue/src/assets/fonts/Roboto/OFL.txt
	renamed:    spa/vue/src/assets/fonts/Roboto/README.txt -> serv_spa/spa/vue/src/assets/fonts/Roboto/README.txt
	renamed:    spa/vue/src/assets/fonts/Roboto/Roboto-Italic-VariableFont_wdth,wght.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/Roboto-Italic-VariableFont_wdth,wght.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/Roboto-Italic.woff -> serv_spa/spa/vue/src/assets/fonts/Roboto/Roboto-Italic.woff
	renamed:    spa/vue/src/assets/fonts/Roboto/Roboto-Italic.woff2 -> serv_spa/spa/vue/src/assets/fonts/Roboto/Roboto-Italic.woff2
	renamed:    spa/vue/src/assets/fonts/Roboto/Roboto-Regular.woff -> serv_spa/spa/vue/src/assets/fonts/Roboto/Roboto-Regular.woff
	renamed:    spa/vue/src/assets/fonts/Roboto/Roboto-Regular.woff2 -> serv_spa/spa/vue/src/assets/fonts/Roboto/Roboto-Regular.woff2
	renamed:    spa/vue/src/assets/fonts/Roboto/Roboto-VariableFont_wdth,wght.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/Roboto-VariableFont_wdth,wght.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/hinted-Roboto-Italic.eot -> serv_spa/spa/vue/src/assets/fonts/Roboto/hinted-Roboto-Italic.eot
	renamed:    spa/vue/src/assets/fonts/Roboto/hinted-Roboto-Italic.svg -> serv_spa/spa/vue/src/assets/fonts/Roboto/hinted-Roboto-Italic.svg
	renamed:    spa/vue/src/assets/fonts/Roboto/hinted-Roboto-Italic.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/hinted-Roboto-Italic.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/hinted-Roboto-Regular.eot -> serv_spa/spa/vue/src/assets/fonts/Roboto/hinted-Roboto-Regular.eot
	renamed:    spa/vue/src/assets/fonts/Roboto/hinted-Roboto-Regular.svg -> serv_spa/spa/vue/src/assets/fonts/Roboto/hinted-Roboto-Regular.svg
	renamed:    spa/vue/src/assets/fonts/Roboto/hinted-Roboto-Regular.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/hinted-Roboto-Regular.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/static/Roboto-Black.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto-Black.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/static/Roboto-BlackItalic.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto-BlackItalic.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/static/Roboto-Bold.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto-Bold.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/static/Roboto-BoldItalic.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto-BoldItalic.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/static/Roboto-ExtraBold.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto-ExtraBold.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/static/Roboto-ExtraBoldItalic.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto-ExtraBoldItalic.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/static/Roboto-ExtraLight.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto-ExtraLight.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/static/Roboto-ExtraLightItalic.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto-ExtraLightItalic.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/static/Roboto-Italic.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto-Italic.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/static/Roboto-Light.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto-Light.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/static/Roboto-LightItalic.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto-LightItalic.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/static/Roboto-Medium.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto-Medium.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/static/Roboto-MediumItalic.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto-MediumItalic.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/static/Roboto-Regular.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto-Regular.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/static/Roboto-SemiBold.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto-SemiBold.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/static/Roboto-SemiBoldItalic.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto-SemiBoldItalic.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/static/Roboto-Thin.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto-Thin.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/static/Roboto-ThinItalic.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto-ThinItalic.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-Black.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-Black.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-BlackItalic.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-BlackItalic.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-Bold.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-Bold.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-BoldItalic.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-BoldItalic.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-ExtraBold.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-ExtraBold.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-ExtraBoldItalic.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-ExtraBoldItalic.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-ExtraLight.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-ExtraLight.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-ExtraLightItalic.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-ExtraLightItalic.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-Italic.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-Italic.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-Light.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-Light.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-LightItalic.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-LightItalic.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-Medium.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-Medium.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-MediumItalic.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-MediumItalic.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-Regular.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-Regular.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-SemiBold.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-SemiBold.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-SemiBoldItalic.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-SemiBoldItalic.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-Thin.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-Thin.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-ThinItalic.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-ThinItalic.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-Black.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-Black.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-BlackItalic.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-BlackItalic.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-Bold.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-Bold.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-BoldItalic.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-BoldItalic.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-ExtraBold.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-ExtraBold.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-ExtraBoldItalic.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-ExtraBoldItalic.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-ExtraLight.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-ExtraLight.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-ExtraLightItalic.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-ExtraLightItalic.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-Italic.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-Italic.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-Light.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-Light.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-LightItalic.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-LightItalic.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-Medium.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-Medium.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-MediumItalic.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-MediumItalic.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-Regular.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-Regular.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-SemiBold.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-SemiBold.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-SemiBoldItalic.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-SemiBoldItalic.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-Thin.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-Thin.ttf
	renamed:    spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-ThinItalic.ttf -> serv_spa/spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-ThinItalic.ttf
	renamed:    spa/vue/src/assets/linksStyle.css -> serv_spa/spa/vue/src/assets/linksStyle.css
	renamed:    spa/vue/src/assets/main.css -> serv_spa/spa/vue/src/assets/main.css
	renamed:    spa/vue/src/auth/axios.js -> serv_spa/spa/vue/src/auth/axios.js
	renamed:    spa/vue/src/auth/services/auth.service.js -> serv_spa/spa/vue/src/auth/services/auth.service.js
	renamed:    spa/vue/src/auth/services/authService.js -> serv_spa/spa/vue/src/auth/services/authService.js
	renamed:    spa/vue/src/auth/stores/auth.store.js -> serv_spa/spa/vue/src/auth/stores/auth.store.js
	renamed:    spa/vue/src/auth/stores/store.js -> serv_spa/spa/vue/src/auth/stores/store.js
	renamed:    spa/vue/src/auth/vueauth/auth.js -> serv_spa/spa/vue/src/auth/vueauth/auth.js
	renamed:    spa/vue/src/auth/watch.js -> serv_spa/spa/vue/src/auth/watch.js
	renamed:    spa/vue/src/components/about/about.vue -> serv_spa/spa/vue/src/components/about/about.vue
	renamed:    spa/vue/src/components/about/commits.vue -> serv_spa/spa/vue/src/components/about/commits.vue
	renamed:    spa/vue/src/components/about/developers.vue -> serv_spa/spa/vue/src/components/about/developers.vue
	renamed:    spa/vue/src/components/about/filosofy.vue -> serv_spa/spa/vue/src/components/about/filosofy.vue
	renamed:    spa/vue/src/components/feetback/feetback.vue -> serv_spa/spa/vue/src/components/feetback/feetback.vue
	renamed:    spa/vue/src/components/footerB/footerB.vue -> serv_spa/spa/vue/src/components/footerB/footerB.vue
	renamed:    spa/vue/src/components/header/darkThemeToggle.vue -> serv_spa/spa/vue/src/components/header/darkThemeToggle.vue
	renamed:    spa/vue/src/components/header/fullHeader.vue -> serv_spa/spa/vue/src/components/header/fullHeader.vue
	renamed:    spa/vue/src/components/header/headerMemu.vue -> serv_spa/spa/vue/src/components/header/headerMemu.vue
	renamed:    spa/vue/src/components/header/logo-rl-about.vue -> serv_spa/spa/vue/src/components/header/logo-rl-about.vue
	renamed:    spa/vue/src/components/header/toggleMenu.vue -> serv_spa/spa/vue/src/components/header/toggleMenu.vue
	renamed:    spa/vue/src/components/images/YalArbaLogo300.png -> serv_spa/spa/vue/src/components/images/YalArbaLogo300.png
	renamed:    spa/vue/src/components/images/icons/arrow_back_24dp_E8EAED_FILL0_wght400_GRAD0_opsz24.svg -> serv_spa/spa/vue/src/components/images/icons/arrow_back_24dp_E8EAED_FILL0_wght400_GRAD0_opsz24.svg
	renamed:    spa/vue/src/components/images/icons/arrow_forward_24dp_E8EAED_FILL0_wght400_GRAD0_opsz24.svg -> serv_spa/spa/vue/src/components/images/icons/arrow_forward_24dp_E8EAED_FILL0_wght400_GRAD0_opsz24.svg
	renamed:    spa/vue/src/components/images/icons/close_24dp_E8EAED_FILL0_wght400_GRAD0_opsz24.svg -> serv_spa/spa/vue/src/components/images/icons/close_24dp_E8EAED_FILL0_wght400_GRAD0_opsz24.svg
	renamed:    spa/vue/src/components/images/icons/home_24dp_E8EAED_FILL0_wght400_GRAD0_opsz24.svg -> serv_spa/spa/vue/src/components/images/icons/home_24dp_E8EAED_FILL0_wght400_GRAD0_opsz24.svg
	renamed:    spa/vue/src/components/images/icons/menu_24dp_E8EAED_FILL0_wght400_GRAD0_opsz24.svg -> serv_spa/spa/vue/src/components/images/icons/menu_24dp_E8EAED_FILL0_wght400_GRAD0_opsz24.svg
	renamed:    spa/vue/src/components/images/logo150x150.png -> serv_spa/spa/vue/src/components/images/logo150x150.png
	renamed:    spa/vue/src/components/images/photo_2025-01-25_05-57-24.jpg -> serv_spa/spa/vue/src/components/images/photo_2025-01-25_05-57-24.jpg
	renamed:    spa/vue/src/components/images/restObject.jpg -> serv_spa/spa/vue/src/components/images/restObject.jpg
	renamed:    spa/vue/src/components/inout/inout.vue -> serv_spa/spa/vue/src/components/inout/inout.vue
	renamed:    spa/vue/src/components/inout/registration.vue -> serv_spa/spa/vue/src/components/inout/registration.vue
	renamed:    spa/vue/src/components/profile/profile.vue -> serv_spa/spa/vue/src/components/profile/profile.vue
	renamed:    spa/vue/src/components/profile/profileEdit.vue -> serv_spa/spa/vue/src/components/profile/profileEdit.vue
	renamed:    spa/vue/src/components/restObject/restObject.vue -> serv_spa/spa/vue/src/components/restObject/restObject.vue
	renamed:    spa/vue/src/components/restObject/restObjectEdit.vue -> serv_spa/spa/vue/src/components/restObject/restObjectEdit.vue
	renamed:    spa/vue/src/components/restObject/restOjbectAdd.vue -> serv_spa/spa/vue/src/components/restObject/restOjbectAdd.vue
	renamed:    spa/vue/src/components/saerch_results/results.vue -> serv_spa/spa/vue/src/components/saerch_results/results.vue
	renamed:    spa/vue/src/components/searchLine/searchLine.vue -> serv_spa/spa/vue/src/components/searchLine/searchLine.vue
	renamed:    spa/vue/src/components/settings.vue -> serv_spa/spa/vue/src/components/settings.vue
	renamed:    spa/vue/src/components/support.vue -> serv_spa/spa/vue/src/components/support.vue
	renamed:    spa/vue/src/locales/bak.json -> serv_spa/spa/vue/src/locales/bak.json
	renamed:    spa/vue/src/locales/en.json -> serv_spa/spa/vue/src/locales/en.json
	renamed:    spa/vue/src/locales/i18n.js -> serv_spa/spa/vue/src/locales/i18n.js
	renamed:    spa/vue/src/locales/langToggle.vue -> serv_spa/spa/vue/src/locales/langToggle.vue
	renamed:    spa/vue/src/locales/languages.json -> serv_spa/spa/vue/src/locales/languages.json
	renamed:    spa/vue/src/locales/ru.json -> serv_spa/spa/vue/src/locales/ru.json
	renamed:    spa/vue/src/locales/tat.json -> serv_spa/spa/vue/src/locales/tat.json
	renamed:    spa/vue/src/main.js -> serv_spa/spa/vue/src/main.js
	renamed:    spa/vue/src/router/index.js -> serv_spa/spa/vue/src/router/index.js
	renamed:    spa/vue/src/views/AboutView.vue -> serv_spa/spa/vue/src/views/AboutView.vue
	renamed:    spa/vue/src/views/FeetbackView.vue -> serv_spa/spa/vue/src/views/FeetbackView.vue
	renamed:    spa/vue/src/views/FilosofyView.vue -> serv_spa/spa/vue/src/views/FilosofyView.vue
	renamed:    spa/vue/src/views/HomeView.vue -> serv_spa/spa/vue/src/views/HomeView.vue
	renamed:    spa/vue/src/views/LogInView.vue -> serv_spa/spa/vue/src/views/LogInView.vue
	renamed:    spa/vue/src/views/ProfileView.vue -> serv_spa/spa/vue/src/views/ProfileView.vue
	renamed:    spa/vue/src/views/RegistrationView.vue -> serv_spa/spa/vue/src/views/RegistrationView.vue
	renamed:    spa/vue/src/views/RestObjectView.vue -> serv_spa/spa/vue/src/views/RestObjectView.vue
	renamed:    spa/vue/src/views/ResultsView.vue -> serv_spa/spa/vue/src/views/ResultsView.vue
	renamed:    spa/vue/src/views/SettingsView.vue -> serv_spa/spa/vue/src/views/SettingsView.vue
	renamed:    spa/vue/src/views/SupportView.vue -> serv_spa/spa/vue/src/views/SupportView.vue
	renamed:    spa/vue/tailwind.config.js -> serv_spa/spa/vue/tailwind.config.js
	renamed:    spa/vue/vite.config.js -> serv_spa/spa/vue/vite.config.js
	deleted:    spa/vue/Makefile
divite spa service
2025-07-31 10:10:42 +05:00
valitovgaziz 4fa5921382 modified: docker-compose.yaml
new file:   serv_migration/docker-compose.yml
	renamed:    migrator/Dockerfile -> serv_migration/migrator/Dockerfile
	renamed:    migrator/go.mod -> serv_migration/migrator/go.mod
	renamed:    migrator/go.sum -> serv_migration/migrator/go.sum
	renamed:    migrator/migrations/20240819162009_create_users_table.sql -> serv_migration/migrator/migrations/20240819162009_create_users_table.sql
	renamed:    migrator/migrations/20240825004755_add_column_role_to_users.sql -> serv_migration/migrator/migrations/20240825004755_add_column_role_to_users.sql
moove migrator into serv_migrator directory
create new docker-compose.yaml
2025-07-30 23:48:25 +05:00
valitovgaziz 2ff3d8b76b modified: .env
modified:   README.md
	deleted:    api/Dockerfile
	deleted:    api/Makefile
	deleted:    api/bin/api.exe
	deleted:    api/cmd/main.go
	deleted:    api/go.mod
	deleted:    api/go.sum
	deleted:    api/src/configs/APIserver.go
	deleted:    api/src/configs/DBconfig.go
	deleted:    api/src/initializers/PGQL_DB.go
	deleted:    api/src/initializers/Routing.go
	deleted:    api/src/models/Contacts.go
	deleted:    api/src/models/Essence.go
	deleted:    api/src/models/Point.go
	deleted:    api/src/models/authDataStructs.go
	deleted:    api/src/models/user.go
	deleted:    api/src/rt/admin/users.go
	deleted:    api/src/rt/auth/Login.go
	deleted:    api/src/rt/auth/Registr.go
	deleted:    api/src/rt/auth/authAdminMiddlware.go
	deleted:    api/src/rt/auth/authMiddleware.go
	deleted:    api/src/rt/auth/auth_test.go
	deleted:    api/src/rt/prf/AuthUserMiddleWare.go
	deleted:    api/src/rt/prf/profile.go
	deleted:    api/src/rt/srch/search.go
	deleted:    api/src/storages/psql/psql.go
	deleted:    db/docker-compose.yml
	modified:   docker-compose.yaml
plan into read.me
2025-07-30 23:11:37 +05:00
valitovgaziz 9b78a3cc11 new file: api/Dockerfile
new file:   api/bin/api.exe
	new file:   api/cmd/main.go
	new file:   api/go.mod
	new file:   api/go.sum
	new file:   api/src/configs/APIserver.go
	new file:   api/src/configs/DBconfig.go
	new file:   api/src/initializers/PGQL_DB.go
	new file:   api/src/initializers/Routing.go
	new file:   api/src/models/Contacts.go
	new file:   api/src/models/Essence.go
	new file:   api/src/models/Point.go
	new file:   api/src/models/authDataStructs.go
	new file:   api/src/models/user.go
	new file:   api/src/rt/admin/users.go
	new file:   api/src/rt/auth/Login.go
	new file:   api/src/rt/auth/Registr.go
	new file:   api/src/rt/auth/authAdminMiddlware.go
	new file:   api/src/rt/auth/authMiddleware.go
	new file:   api/src/rt/auth/auth_test.go
	new file:   api/src/rt/prf/AuthUserMiddleWare.go
	new file:   api/src/rt/prf/profile.go
	new file:   api/src/rt/srch/search.go
	new file:   api/src/storages/psql/psql.go
	new file:   docker-compose.yml

 Changes not staged for commit:
	modified:   ../.env
	deleted:    ../api/Dockerfile
	deleted:    ../api/Makefile
	deleted:    ../api/bin/api.exe
	deleted:    ../api/cmd/main.go
	deleted:    ../api/go.mod
	deleted:    ../api/go.sum
	deleted:    ../api/src/configs/APIserver.go
	deleted:    ../api/src/configs/DBconfig.go
	deleted:    ../api/src/initializers/PGQL_DB.go
	deleted:    ../api/src/initializers/Routing.go
	deleted:    ../api/src/models/Contacts.go
	deleted:    ../api/src/models/Essence.go
	deleted:    ../api/src/models/Point.go
	deleted:    ../api/src/models/authDataStructs.go
	deleted:    ../api/src/models/user.go
	deleted:    ../api/src/rt/admin/users.go
	deleted:    ../api/src/rt/auth/Login.go
	deleted:    ../api/src/rt/auth/Registr.go
	deleted:    ../api/src/rt/auth/authAdminMiddlware.go
	deleted:    ../api/src/rt/auth/authMiddleware.go
	deleted:    ../api/src/rt/auth/auth_test.go
	deleted:    ../api/src/rt/prf/AuthUserMiddleWare.go
	deleted:    ../api/src/rt/prf/profile.go
	deleted:    ../api/src/rt/srch/search.go
	deleted:    ../api/src/storages/psql/psql.go
	deleted:    ../db/docker-compose.yml
	modified:   ../docker-compose.yaml
movve file tino new directo for towo service rest api and d
2025-07-30 23:02:52 +05:00
valitovgaziz afe7b27cce modified: ../.env
modified:   ../docker-compose.yaml
specify the db service into db directory
2025-07-30 21:52:24 +05:00
valitovgaziz 9ef0953fd0 modified: .gitignore
json config modifaing
2025-07-26 08:12:37 +05:00
Gaziz Valitov 4f0251629e изменено: certbot/Dockerfile
change line with permits for files
2025-07-26 07:49:37 +05:00
valitovgaziz 1b1400daa1 modified: certbot/config/certbot.ini
add domains line with all domains that we use
2025-07-22 04:55:06 +05:00
valitovgaziz acd7cb5441 renamed: spa/vue/dist/assets/AboutView-CTufbipe.js -> spa/vue/dist/assets/AboutView-B7yELX4Y.js
renamed:    spa/vue/dist/assets/FeetbackView-B9vw7apk.js -> spa/vue/dist/assets/FeetbackView-CXGcB7N7.js
	deleted:    spa/vue/dist/assets/HomeView-D1IUYyPG.js
	new file:   spa/vue/dist/assets/HomeView-zKlroz3N.js
	renamed:    spa/vue/dist/assets/LogInView-BJ2czjmX.js -> spa/vue/dist/assets/LogInView-CCzg48Go.js
	renamed:    spa/vue/dist/assets/ProfileView-DOfM9Ta9.js -> spa/vue/dist/assets/ProfileView-BuVzjMSP.js
	renamed:    spa/vue/dist/assets/RegistrationView-BgG_0HRE.js -> spa/vue/dist/assets/RegistrationView-C0Di_tAT.js
	renamed:    spa/vue/dist/assets/RestObjectView-CgfhSbZM.js -> spa/vue/dist/assets/RestObjectView-CuQAHRrm.js
	renamed:    spa/vue/dist/assets/ResultsView-Czz66SUa.js -> spa/vue/dist/assets/ResultsView-C1CSY9zD.js
	renamed:    spa/vue/dist/assets/SettingsView-C01SJVt6.js -> spa/vue/dist/assets/SettingsView-BlE1G-Ym.js
	renamed:    spa/vue/dist/assets/SupportView-Hwog53I5.js -> spa/vue/dist/assets/SupportView-B_EO3gSX.js
	new file:   spa/vue/dist/assets/YalArbaLogo300-BGWtRO6S.png
	renamed:    spa/vue/dist/assets/footerB-B_sYJCD0.js -> spa/vue/dist/assets/footerB-DAi6c01a.js
	renamed:    spa/vue/dist/assets/index-BIfu7HcU.js -> spa/vue/dist/assets/index-DccGIixi.js
	renamed:    spa/vue/dist/assets/searchLine-BlO5YIbG.js -> spa/vue/dist/assets/searchLine-3vDx-8Ye.js
	modified:   spa/vue/dist/index.html
	modified:   spa/vue/index.html
	new file:   spa/vue/src/components/images/YalArbaLogo300.png
Rebuild vue app
2025-07-20 18:05:48 +05:00
valitovgaziz 88357dc3cc modified: certbot/scripts/init-certbot.sh
delete lines with install crons in this file
2025-07-20 09:21:19 +05:00
valitovgaziz a6952eac5d modified: certbot/config/certbot.ini
delete line with domains
2025-07-20 09:19:07 +05:00
valitovgaziz b0b0ecd261 modified: .env
modified:   docker-compose.yaml
add ALL_DOMAINS var to files
2025-07-20 09:11:06 +05:00
valitovgaziz 32d7ad7dad modified: Makefile
remove profiles and test commands from Makefile
2025-07-20 09:04:32 +05:00
valitovgaziz 34f5bc62bc modified: docker-compose.yaml
delete profiles, comment not useing for now services
2025-07-20 09:03:13 +05:00
valitovgaziz 845f7e476f modified: certbot/Dockerfile
do not install cron couse it is being in inmage from start
2025-07-20 08:43:13 +05:00
valitovgaziz 67ef797d04 modified: certbot/Dockerfile
change apt to apk for install crontab into image
2025-07-20 01:38:41 +05:00
valitovgaziz ceefb20769 modified: nginx/nginx-ssl.conf
Add some ssl settings, fix bags with new easysite102.ru doman settings
2025-07-20 01:33:19 +05:00
valitovgaziz 24752ff05f modified: certbot/Dockerfile
new file:   certbot/scripts/crontab.txt
	modified:   certbot/scripts/init-certbot.sh
Add crontab task file, add into build cront install command
2025-07-19 17:50:36 +05:00
valitovgaziz 1a2cfa2104 modified: certbot/Dockerfile
Change Dockerfile for new files renewEsysite102.sh
2025-07-19 17:34:54 +05:00
valitovgaziz bf3cd332f6 modified: .env
new file:   certbot/scripts/renewEasysite102.sh
add easysite102 domans variable into .env file
create file for renew easysite102.ru dome renewEasysite102.sh
2025-07-19 10:28:15 +05:00
valitovgaziz 745e539562 modified: certbot/scripts/checkRenewCerts.sh
modified:   certbot/scripts/init-certbot.sh
add easysite102 to cechRenew and init-cerbot scripts
2025-07-19 10:24:16 +05:00
valitovgaziz 0d7cf5dcf9 modified: certbot/config/certbot.ini
add esysite102.ru to certbot.ini file
2025-07-19 10:19:05 +05:00
valitovgaziz 92035f9079 modified: docker-compose.yaml
modified:   easySite/easy-site/build/index.html
add into docker-compose.yaml mount line easysite
2025-07-19 10:08:39 +05:00
valitovgaziz 59b9007be5 modified: nginx/nginx-ssl.conf
add to nginx config the config for easysite102.ru
2025-07-19 06:54:48 +03:00
valitovgaziz 3f1b889961 modified: nginx/nginx-ssl.conf
Add try_files $uri $uri/ /index.html; to nginx-ssl.conf
2025-07-19 03:04:16 +03:00
valitovgaziz f6d512dbde new file: build/asset-manifest.json
new file:   build/index.html
	new file:   build/static/js/main.9a8f7abd.js
	new file:   build/static/js/main.9a8f7abd.js.LICENSE.txt
	new file:   build/static/js/main.9a8f7abd.js.map
	new file:   postcss.config.js
	deleted:    public/favicon.ico
	deleted:    public/logo192.png
	deleted:    public/logo512.png
	deleted:    public/manifest.json
	deleted:    public/robots.txt
	new file:   tailwind.config.js
Build project, delete start favicons
2025-07-19 04:43:00 +05:00
valitovgaziz 5898bc6d5a modified: .gitignore
new file:   easySite/easy-site/README.md
	new file:   easySite/easy-site/package-lock.json
	new file:   easySite/easy-site/package.json
	new file:   easySite/easy-site/public/favicon.ico
	new file:   easySite/easy-site/public/index.html
	new file:   easySite/easy-site/public/logo192.png
	new file:   easySite/easy-site/public/logo512.png
	new file:   easySite/easy-site/public/manifest.json
	new file:   easySite/easy-site/public/robots.txt
	new file:   easySite/easy-site/src/App.css
	new file:   easySite/easy-site/src/App.js
	new file:   easySite/easy-site/src/App.test.js
	new file:   easySite/easy-site/src/contexts/AuthContext.js
	new file:   easySite/easy-site/src/contexts/useAuth.js
	new file:   easySite/easy-site/src/index.css
	new file:   easySite/easy-site/src/index.js
	new file:   easySite/easy-site/src/logo.svg
	new file:   easySite/easy-site/src/pages/AddObject.js
	new file:   easySite/easy-site/src/pages/Dashboard.js
	new file:   easySite/easy-site/src/pages/EditObject.js
	new file:   easySite/easy-site/src/pages/Home.js
	new file:   easySite/easy-site/src/pages/Login.js
	new file:   easySite/easy-site/src/pages/Register.js
	new file:   easySite/easy-site/src/reportWebVitals.js
	new file:   easySite/easy-site/src/setupTests.js
	new file:   package-lock.json
	new file:   package.json
	modified:   valitovgaziz/html/saveContactsButtonStyle.css
Add the easy_site site on react
2025-07-18 16:26:11 +05:00
valitovgaziz 1812b2a8af new file: valitovgaziz/html/darkTheme.css
new file:   valitovgaziz/html/darkThemeToggle.js
	modified:   valitovgaziz/html/index.html
	modified:   valitovgaziz/html/scripts.js
	modified:   valitovgaziz/html/style.css
Add the dark theme into valitovgaziz site
2025-07-17 16:29:34 +05:00
valitovgaziz a5f7810d0c modified: valitovgaziz/html/style.css
fix bag with main page min-width
2025-07-17 15:35:45 +05:00
valitovgaziz 0fe2639a6d renamed: valitovgaziz/html/saveContStyle.css -> valitovgaziz/html/saveContactsButtonStyle.css
modified:   valitovgaziz/html/style.css
change the name of button style.css and import to main css file
2025-07-17 11:48:40 +05:00
valitovgaziz 52ca3a27a3 modified: valitovgaziz/html/index.html
add section about me autobiografy
2025-07-17 06:26:25 +05:00
valitovgaziz cb7a083850 modified: valitovgaziz/html/index.html
fix skills into valitovgaziz stite
2025-07-17 06:10:06 +05:00
valitovgaziz c5d93e9af3 modified: valitovgaziz/html/index.html
Fix time and add to ОБразование section into valitovgaziz site
2025-07-17 06:07:50 +05:00
valitovgaziz 209de136b2 modified: valitovgaziz/html/index.html
fix Course and serts into valitovgaziz site
2025-07-17 06:04:43 +05:00
valitovgaziz 98389394f4 modified: spa/vue/src/router/index.js
change base_url to /
2025-07-14 17:49:35 +05:00
valitovgaziz 151dbaf220 renamed: spa/vue/servers/authserver.js -> node_servers/authserver.js
renamed:    spa/vue/servers/feedbackserver.js -> node_servers/feedbackserver.js
	renamed:    spa/vue/servers/techsupportserver.js -> node_servers/techsupportserver.js
add and set to one more Docker coneiner for node servers
2025-07-14 16:18:01 +05:00
valitovgaziz 44da0ee296 modified: Makefile
modified:   docker-compose.yaml
change profile to prod, add to kk_db kk profile
2025-07-14 15:49:44 +05:00
valitovgaziz c659dd0f4b modified: docker-compose.yaml
set ngixn config for use vue3 dist for production
2025-07-14 13:31:09 +05:00
valitovgaziz 5ff8a67d20 modified: .gitignore
modified:   spa/Dockerfile
	deleted:    spa/vue/.gitignore
	new file:   spa/vue/dist/assets/AboutView-C1FaiPPz.css
	new file:   spa/vue/dist/assets/AboutView-CTufbipe.js
	new file:   spa/vue/dist/assets/FeetbackView-B9vw7apk.js
	new file:   spa/vue/dist/assets/FeetbackView-Csl9V8Bc.css
	new file:   spa/vue/dist/assets/HomeView-C0JCTEr_.css
	new file:   spa/vue/dist/assets/HomeView-D1IUYyPG.js
	new file:   spa/vue/dist/assets/LogInView-BJ2czjmX.js
	new file:   spa/vue/dist/assets/LogInView-Co-3gSCv.css
	new file:   spa/vue/dist/assets/OpenSans-Italic-Cv5d4RQ-.woff
	new file:   spa/vue/dist/assets/OpenSans-Italic-DNMplG0v.woff2
	new file:   spa/vue/dist/assets/OpenSans-Regular-BT0WUJf-.woff2
	new file:   spa/vue/dist/assets/OpenSans-Regular-C74AleX8.woff
	new file:   spa/vue/dist/assets/ProfileView-COXJamv9.css
	new file:   spa/vue/dist/assets/ProfileView-DOfM9Ta9.js
	new file:   spa/vue/dist/assets/RegistrationView-BgG_0HRE.js
	new file:   spa/vue/dist/assets/RegistrationView-D_FgJHPY.css
	new file:   spa/vue/dist/assets/RestObjectView-CgfhSbZM.js
	new file:   spa/vue/dist/assets/RestObjectView-DU_QDLXy.css
	new file:   spa/vue/dist/assets/ResultsView-Czz66SUa.js
	new file:   spa/vue/dist/assets/ResultsView-ijT9kjlC.css
	new file:   spa/vue/dist/assets/Roboto-Italic-DS1crIJz.woff2
	new file:   spa/vue/dist/assets/Roboto-Italic-uZr_9iaA.woff
	new file:   spa/vue/dist/assets/Roboto-Regular-jtJqKL8A.woff2
	new file:   spa/vue/dist/assets/Roboto-Regular-n8vMMKqC.woff
	new file:   spa/vue/dist/assets/SettingsView-C01SJVt6.js
	new file:   spa/vue/dist/assets/SettingsView-FlTPuBSa.css
	new file:   spa/vue/dist/assets/SupportView-DF9LsVNx.css
	new file:   spa/vue/dist/assets/SupportView-Hwog53I5.js
	new file:   spa/vue/dist/assets/footerB-B_sYJCD0.js
	new file:   spa/vue/dist/assets/footerB-D_GT2umv.css
	new file:   spa/vue/dist/assets/hinted-OpenSans-Italic-BD2qe0ib.ttf
	new file:   spa/vue/dist/assets/hinted-OpenSans-Regular-DZwYhniE.ttf
	new file:   spa/vue/dist/assets/hinted-Roboto-Italic-CDlkg4IH.ttf
	new file:   spa/vue/dist/assets/hinted-Roboto-Regular-CFXqJ5DR.ttf
	new file:   spa/vue/dist/assets/index-B44ZMC8Y.css
	new file:   spa/vue/dist/assets/index-BIfu7HcU.js
	new file:   spa/vue/dist/assets/logo150x150-De-v7fGb.png
	new file:   spa/vue/dist/assets/photo_2025-01-25_05-57-24-BwgIchq6.jpg
	new file:   spa/vue/dist/assets/restObject-CcS1cBol.jpg
	new file:   spa/vue/dist/assets/searchLine-BlO5YIbG.js
	new file:   spa/vue/dist/assets/searchLine-Bqe_ccJt.css
	new file:   spa/vue/dist/index.html
add dist for production into commit, delete .gitignore spa
2025-07-14 13:28:40 +05:00
valitovgaziz 2d07814bb1 modified: .gitignore
new file:   spa/vue/.gitignore
	new file:   spa/vue/Makefile
	new file:   spa/vue/README.md
	new file:   spa/vue/advices.txt
	new file:   spa/vue/index.html
	new file:   spa/vue/jsconfig.json
	new file:   spa/vue/package-lock.json
	new file:   spa/vue/package.json
	new file:   spa/vue/servers/authserver.js
	new file:   spa/vue/servers/feedbackserver.js
	new file:   spa/vue/servers/techsupportserver.js
	new file:   spa/vue/src/App.vue
	new file:   spa/vue/src/assets/colors.css
	new file:   spa/vue/src/assets/fonts.css
	new file:   spa/vue/src/assets/fonts/Open_Sans/OFL.txt
	new file:   spa/vue/src/assets/fonts/Open_Sans/OpenSans-Italic-VariableFont_wdth,wght.ttf
	new file:   spa/vue/src/assets/fonts/Open_Sans/OpenSans-Italic.woff
	new file:   spa/vue/src/assets/fonts/Open_Sans/OpenSans-Italic.woff2
	new file:   spa/vue/src/assets/fonts/Open_Sans/OpenSans-Regular.woff
	new file:   spa/vue/src/assets/fonts/Open_Sans/OpenSans-Regular.woff2
	new file:   spa/vue/src/assets/fonts/Open_Sans/OpenSans-VariableFont_wdth,wght.ttf
	new file:   spa/vue/src/assets/fonts/Open_Sans/README.txt
	new file:   spa/vue/src/assets/fonts/Open_Sans/hinted-OpenSans-Italic.eot
	new file:   spa/vue/src/assets/fonts/Open_Sans/hinted-OpenSans-Italic.svg
	new file:   spa/vue/src/assets/fonts/Open_Sans/hinted-OpenSans-Italic.ttf
	new file:   spa/vue/src/assets/fonts/Open_Sans/hinted-OpenSans-Regular.eot
	new file:   spa/vue/src/assets/fonts/Open_Sans/hinted-OpenSans-Regular.svg
	new file:   spa/vue/src/assets/fonts/Open_Sans/hinted-OpenSans-Regular.ttf
	new file:   spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-Bold.ttf
	new file:   spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-BoldItalic.ttf
	new file:   spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-ExtraBold.ttf
	new file:   spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-ExtraBoldItalic.ttf
	new file:   spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-Italic.ttf
	new file:   spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-Light.ttf
	new file:   spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-LightItalic.ttf
	new file:   spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-Medium.ttf
	new file:   spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-MediumItalic.ttf
	new file:   spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-Regular.ttf
	new file:   spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-SemiBold.ttf
	new file:   spa/vue/src/assets/fonts/Open_Sans/static/OpenSans-SemiBoldItalic.ttf
	new file:   spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-Bold.ttf
	new file:   spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-BoldItalic.ttf
	new file:   spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-ExtraBold.ttf
	new file:   spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-ExtraBoldItalic.ttf
	new file:   spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-Italic.ttf
	new file:   spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-Light.ttf
	new file:   spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-LightItalic.ttf
	new file:   spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-Medium.ttf
	new file:   spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-MediumItalic.ttf
	new file:   spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-Regular.ttf
	new file:   spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-SemiBold.ttf
	new file:   spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_Condensed-SemiBoldItalic.ttf
	new file:   spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-Bold.ttf
	new file:   spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-BoldItalic.ttf
	new file:   spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-ExtraBold.ttf
	new file:   spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-ExtraBoldItalic.ttf
	new file:   spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-Italic.ttf
	new file:   spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-Light.ttf
	new file:   spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-LightItalic.ttf
	new file:   spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-Medium.ttf
	new file:   spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-MediumItalic.ttf
	new file:   spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-Regular.ttf
	new file:   spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-SemiBold.ttf
	new file:   spa/vue/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-SemiBoldItalic.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/OFL.txt
	new file:   spa/vue/src/assets/fonts/Roboto/README.txt
	new file:   spa/vue/src/assets/fonts/Roboto/Roboto-Italic-VariableFont_wdth,wght.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/Roboto-Italic.woff
	new file:   spa/vue/src/assets/fonts/Roboto/Roboto-Italic.woff2
	new file:   spa/vue/src/assets/fonts/Roboto/Roboto-Regular.woff
	new file:   spa/vue/src/assets/fonts/Roboto/Roboto-Regular.woff2
	new file:   spa/vue/src/assets/fonts/Roboto/Roboto-VariableFont_wdth,wght.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/hinted-Roboto-Italic.eot
	new file:   spa/vue/src/assets/fonts/Roboto/hinted-Roboto-Italic.svg
	new file:   spa/vue/src/assets/fonts/Roboto/hinted-Roboto-Italic.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/hinted-Roboto-Regular.eot
	new file:   spa/vue/src/assets/fonts/Roboto/hinted-Roboto-Regular.svg
	new file:   spa/vue/src/assets/fonts/Roboto/hinted-Roboto-Regular.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/static/Roboto-Black.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/static/Roboto-BlackItalic.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/static/Roboto-Bold.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/static/Roboto-BoldItalic.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/static/Roboto-ExtraBold.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/static/Roboto-ExtraBoldItalic.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/static/Roboto-ExtraLight.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/static/Roboto-ExtraLightItalic.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/static/Roboto-Italic.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/static/Roboto-Light.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/static/Roboto-LightItalic.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/static/Roboto-Medium.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/static/Roboto-MediumItalic.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/static/Roboto-Regular.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/static/Roboto-SemiBold.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/static/Roboto-SemiBoldItalic.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/static/Roboto-Thin.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/static/Roboto-ThinItalic.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-Black.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-BlackItalic.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-Bold.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-BoldItalic.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-ExtraBold.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-ExtraBoldItalic.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-ExtraLight.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-ExtraLightItalic.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-Italic.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-Light.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-LightItalic.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-Medium.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-MediumItalic.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-Regular.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-SemiBold.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-SemiBoldItalic.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-Thin.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/static/Roboto_Condensed-ThinItalic.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-Black.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-BlackItalic.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-Bold.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-BoldItalic.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-ExtraBold.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-ExtraBoldItalic.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-ExtraLight.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-ExtraLightItalic.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-Italic.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-Light.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-LightItalic.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-Medium.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-MediumItalic.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-Regular.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-SemiBold.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-SemiBoldItalic.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-Thin.ttf
	new file:   spa/vue/src/assets/fonts/Roboto/static/Roboto_SemiCondensed-ThinItalic.ttf
	new file:   spa/vue/src/assets/linksStyle.css
	new file:   spa/vue/src/assets/main.css
	new file:   spa/vue/src/auth/axios.js
	new file:   spa/vue/src/auth/services/auth.service.js
	new file:   spa/vue/src/auth/services/authService.js
	new file:   spa/vue/src/auth/stores/auth.store.js
	new file:   spa/vue/src/auth/stores/store.js
	new file:   spa/vue/src/auth/vueauth/auth.js
	new file:   spa/vue/src/auth/watch.js
	new file:   spa/vue/src/components/about/about.vue
	new file:   spa/vue/src/components/about/commits.vue
	new file:   spa/vue/src/components/about/developers.vue
	new file:   spa/vue/src/components/about/filosofy.vue
	new file:   spa/vue/src/components/feetback/feetback.vue
	new file:   spa/vue/src/components/footerB/footerB.vue
	new file:   spa/vue/src/components/header/darkThemeToggle.vue
	new file:   spa/vue/src/components/header/fullHeader.vue
	new file:   spa/vue/src/components/header/headerMemu.vue
	new file:   spa/vue/src/components/header/logo-rl-about.vue
	new file:   spa/vue/src/components/header/toggleMenu.vue
	new file:   spa/vue/src/components/images/icons/arrow_back_24dp_E8EAED_FILL0_wght400_GRAD0_opsz24.svg
	new file:   spa/vue/src/components/images/icons/arrow_forward_24dp_E8EAED_FILL0_wght400_GRAD0_opsz24.svg
	new file:   spa/vue/src/components/images/icons/close_24dp_E8EAED_FILL0_wght400_GRAD0_opsz24.svg
	new file:   spa/vue/src/components/images/icons/home_24dp_E8EAED_FILL0_wght400_GRAD0_opsz24.svg
	new file:   spa/vue/src/components/images/icons/menu_24dp_E8EAED_FILL0_wght400_GRAD0_opsz24.svg
	new file:   spa/vue/src/components/images/logo150x150.png
	new file:   spa/vue/src/components/images/photo_2025-01-25_05-57-24.jpg
	new file:   spa/vue/src/components/images/restObject.jpg
	new file:   spa/vue/src/components/inout/inout.vue
	new file:   spa/vue/src/components/inout/registration.vue
	new file:   spa/vue/src/components/profile/profile.vue
	new file:   spa/vue/src/components/profile/profileEdit.vue
	new file:   spa/vue/src/components/restObject/restObject.vue
	new file:   spa/vue/src/components/restObject/restObjectEdit.vue
	new file:   spa/vue/src/components/restObject/restOjbectAdd.vue
	new file:   spa/vue/src/components/saerch_results/results.vue
	new file:   spa/vue/src/components/searchLine/searchLine.vue
	new file:   spa/vue/src/components/settings.vue
	new file:   spa/vue/src/components/support.vue
	new file:   spa/vue/src/locales/bak.json
	new file:   spa/vue/src/locales/en.json
	new file:   spa/vue/src/locales/i18n.js
	new file:   spa/vue/src/locales/langToggle.vue
	new file:   spa/vue/src/locales/languages.json
	new file:   spa/vue/src/locales/ru.json
	new file:   spa/vue/src/locales/tat.json
	new file:   spa/vue/src/main.js
	new file:   spa/vue/src/router/index.js
	new file:   spa/vue/src/views/AboutView.vue
	new file:   spa/vue/src/views/FeetbackView.vue
	new file:   spa/vue/src/views/FilosofyView.vue
	new file:   spa/vue/src/views/HomeView.vue
	new file:   spa/vue/src/views/LogInView.vue
	new file:   spa/vue/src/views/ProfileView.vue
	new file:   spa/vue/src/views/RegistrationView.vue
	new file:   spa/vue/src/views/RestObjectView.vue
	new file:   spa/vue/src/views/ResultsView.vue
	new file:   spa/vue/src/views/SettingsView.vue
	new file:   spa/vue/src/views/SupportView.vue
	new file:   spa/vue/tailwind.config.js
	new file:   spa/vue/vite.config.js
add hole project on vue3 for a time
2025-07-14 13:10:18 +05:00
valitovgaziz 49c20b5dc5 new file: spa/app/assets/bage_logo.png
modified:   spa/app/index.html
	new file:   spa/app/styles/mainStyle.css
add mainStyle.css file, add logo icon logo for browser tab
2025-07-14 12:16:47 +05:00
valitovgaziz 4a5c4e3df5 modified: spa/app/index.html
modified:   valitovgaziz/html/index.html
add yandex metrika for yalarba.ru and change metrika for valitovgaziz
2025-07-14 10:42:54 +05:00
valitovgaziz f4f9e57d29 modified: spa/app/index.html
add yandex metrika for yalarba.ru
2025-07-14 10:37:15 +05:00
valitovgaziz 9d09a7d2d1 deleted: spa/app/valitovgaziz/index.html
delete valitovgaziz/index.html with dir
2025-07-14 08:31:47 +03:00
valitovgaziz 28a4822329 modified: valitovgaziz/html/index.html
add yandex metrika counter for valitovgaziz site
2025-07-14 10:30:50 +05:00
valitovgaziz e6ed6450f8 modified: valitovgaziz/html/scripts.js
fix the vcard file data
2025-07-13 06:32:36 +05:00
valitovgaziz 4167668e33 Merge branch 'develop_valitovgaziz_site' into development
modified:   valitovgaziz/html/index.html
	new file:   valitovgaziz/html/saveContStyle.css
	new file:   valitovgaziz/html/scripts.js
	modified:   valitovgaziz/html/style.css
Add save contacts button
2025-07-13 05:49:53 +05:00
valitovgaziz 92faef15f9 modified: valitovgaziz/html/index.html
new file:   valitovgaziz/html/saveContStyle.css
	new file:   valitovgaziz/html/scripts.js
	modified:   valitovgaziz/html/style.css
Add button for save valitovgaziz's contacts by download vCard file
2025-07-13 05:44:14 +05:00
valitovgaziz 9bf1439825 modified: valitovgaziz/html/index.html
fix phone number
2025-07-10 13:35:37 +05:00
valitovgaziz d2b064b5b8 modified: valitovgaziz/html/index.html
Add the filosify of develop tp project
2025-07-10 13:20:04 +05:00
valitovgaziz fc3520de97 modified: valitovgaziz/html/style.css
set min-width body 300px
2025-07-10 11:54:28 +05:00
valitovgaziz 88ef0d4b06 modified: valitovgaziz/html/index.html
modified:   valitovgaziz/html/style.css
change min-width for make page
2025-07-10 01:52:25 +05:00
valitovgaziz ce0965ff68 modified: valitovgaziz/html/index.html
modified:   valitovgaziz/html/style.css
add to site valitovgaziz telegram contacts
2025-07-10 00:24:52 +05:00
valitovgaziz 19ab2ee73e modified: valitovgaziz/html/index.html
new file:   valitovgaziz/html/style.css
Add tyle file and delete style into heade index.html file's
2025-07-09 23:31:07 +05:00
valitovgaziz 859b8d2f69 modified: nginx/nginx-ssl.conf
rebuild nginx service with right configs
2025-07-09 13:35:25 +03:00
valitovgaziz 7368eaa744 modified: docker-compose.yaml
renamed:    nginx/valitovgaziz/html/index.html -> valitovgaziz/html/index.html
fix bag fith path to index.html file for valitovgaziz.ru site
2025-07-09 13:18:19 +03:00
valitovgaziz ccbb533819 new file: nginx/valitovgaziz/html/index.html
create and add index.html file for site valitovgaziz.ru
2025-07-09 13:10:46 +03:00
valitovgaziz 050e9ff9f5 modified: nginx/nginx-ssl.conf
add setring for valitovgaziz config for nginx
2025-07-09 13:05:55 +03:00
valitovgaziz dfa85c805a Merge branch 'add_keycloak' of github.com:valitovgaziz/tp into add_keycloak 2025-07-08 11:17:49 +03:00
valitovgaziz 0da4ffcb7a new file: certbot/scripts/checkRenewCerts.sh
modified:   certbot/scripts/init-certbot.sh add
	renamed:    certbot/scripts/renewAllCerts.sh -> certbot/scripts/renewValitovGazizCert.sh
	new file:   certbot/scripts/renewYalarbaCert.sh
valitovgaziz domen renew&init scripts added
checkRenewCerts for both domains
2025-07-08 13:13:58 +05:00
valitovgaziz 8016e5614e Merge branch 'add_keycloak' of github.com:
valitovgaziz/tp into add_keycloak
2025-07-08 10:30:36 +03:00
valitovgaziz c3ed11738b modified: .env
modified:   certbot/scripts/renewAllCerts.sh
change renewAllCerts to renew only valitovgaziz certs
add to .env DOMAINS_valitovgaziz var
2025-07-08 12:28:17 +05:00
valitovgaziz 13c6c6f556 add copy line into Dockerfile for renewAllCerts.sh
modified:   certbot/Dockerfile
2025-07-08 02:45:27 +03:00
valitovgaziz 6840200e80 add script certbot/scripts/renewAllCerts.sh, it is renwe all certificates 2025-07-08 02:39:03 +03:00
valitovgaziz 7c8d5fb8b9 add valitovgaziz.ru domain into .env file and certbot.ini file 2025-07-08 02:24:51 +03:00
valitovgaziz f96d0b3cc4 add DB config into Dockerfile for keycloak, DC_DB change to postgres 2025-07-08 02:16:50 +03:00
valitovgaziz 17a8e5b2c8 delete prots for certbot into .evn file 2025-07-08 02:03:54 +03:00
valitovgaziz 5b978288a7 nginx proxypass host 2025-07-07 10:57:02 +05:00
valitovgaziz aaac7c183b rechange KC_DB 2025-07-07 10:55:32 +05:00
valitovgaziz d14e86ecb0 change KC_DB to keycloak into docker-compose.yaml, Dockerfile 2025-07-07 08:38:56 +03:00
valitovgaziz 2178feac0a add passthrough, /auth rel path, backchannel false 2025-07-07 10:24:04 +05:00
valitovgaziz 4b62905b0b the colon instead equal
:
2025-07-07 07:55:19 +03:00
valitovgaziz bd4bf424bc add KC hostname and frontendURL into docker-compose.yaml file 2025-07-07 09:53:42 +05:00
valitovgaziz 18c2a93154 fix last 2025-07-07 09:50:40 +05:00
valitovgaziz c05d6327c1 proxy adderss true without forgin and host name strict and http enabled 2025-07-07 09:50:14 +05:00
valitovgaziz ad3dbfb170 fix KC_ADMIN 2025-07-07 09:42:22 +05:00
valitovgaziz 70cca66bd1 rename env vars into docker-compose.yaml file 2025-07-07 09:13:16 +05:00
valitovgaziz 66deed0b0a add DB env vars into Dockerfile 2025-07-07 08:52:04 +05:00
valitovgaziz 97f9cf1ce7 add KECLAOCK frontend url https://yalarba.ru/auth 2025-07-07 06:40:42 +03:00
valitovgaziz 6aefe84008 stable keycloak host, need set admin admin 2025-07-07 06:30:03 +03:00
valitovgaziz 81910ad805 delete the /auth http for keycloak and set http = enable for keyclaok 2025-06-28 02:08:08 +03:00
valitovgaziz e98f44a4d0 Make file delete buildlC row 2025-06-27 05:56:52 +03:00
valitovgaziz f84f4c7155 add crtifectible subdomains for auth.yalarba.ru, www.yalarba.ru 2025-06-23 06:42:18 +03:00
valitovgaziz 25edc04d9c change file certbotini 2025-06-23 06:04:23 +03:00
valitovgaziz a4a62eb3bb add domains into certbot.ini file 2025-06-23 06:02:05 +03:00
valitovgaziz 01eff2d54a 22062025 7 51 50 early morning 2025-06-23 05:50:57 +03:00
valitovgaziz b6dc0a0ef7 change nginx conf for keycloak proxy https 2025-06-22 02:34:30 +03:00
valitovgaziz 88c7a3fb9d Start keycloak but need to fix bag with not found page 2025-06-22 01:53:37 +03:00
valitovgaziz 5e944fca12 add profiles into dokcer-cmpse.ymal file 2025-06-21 23:54:28 +03:00
valitovgaziz fb90fc6d6e add to dokcer compose yaml the keycloak service and DB 2025-06-21 08:08:02 +03:00
valitovgaziz 89d6d62f12 nginx and certbot into containers is start and renew certs by auto 2025-05-31 19:54:35 +03:00
valitovgaziz 81fd806691 change into Dockerfile cerbot -p to 00http-01-port 2025-05-28 13:21:21 +03:00
valitovgaziz cdd5ea6def change the certbot servcie settings into docker-compoe.ymlfile 2025-05-28 13:14:13 +03:00
valitovgaziz d264153f46 add Dockerfile into certbot, hardcod the port for certbot into nginx.conf file 2025-05-28 13:12:12 +03:00
valitovgaziz be0c9ed5a1 rename nginx.conf to nginx.conf.tempate 2025-05-28 12:51:47 +03:00
valitovgaziz 1272476bad remove .vscode, edit .gitignore, README.md 2025-05-28 09:24:01 +03:00
valitovgaziz b1c72b01a4 add alternative port for certbot, change path for spa__app int docker-compose.yml, change Dockerfile 2025-05-27 19:37:12 +03:00
valitovgaziz 3b60cd6317 use the alternative port for cerbot renew certs 2025-05-27 16:02:10 +03:00
valitovgaziz b23aebab4c build one nginx one spa one cerbot and set settings 2025-05-27 14:52:43 +03:00
valitovgaziz a4a282e207 add network , volumes, nginx service 2025-05-26 19:59:50 +03:00
valitovgaziz c197427ab8 create nginx service and add Dcokerfilewith cerbot 2025-05-26 19:35:50 +03:00
valitovgaziz 34f445132c add http and https port into spa service 2025-05-26 08:16:56 +03:00
valitovgaziz 56722431c8 added certbot ervice 2025-05-26 07:43:25 +03:00
valitovgaziz 9e2d25d056 Add restart : unless-stoppped 2025-05-25 18:45:53 +03:00
valitovgaziz 49cef31e2e chanage outterport for spa to 80, rename services 2025-05-25 16:33:28 +03:00
611 changed files with 121251 additions and 1220 deletions
-21
View File
@@ -1,21 +0,0 @@
PGHOST=db
PGPORT=5432
PGUSER=postgres
PGPASSWORD=HnFxccAF3sdUwnI1EkwmXQ==
PGDATABASE=postgres
SSLmode=disable
PGURL='postgres://postgres:HnFxccAF3sdUwnI1EkwmXQ==@db:5432/postgres?sslmode=disable'
# SERVER
SERVER_PORT=8000
SECRET_KEY=lUx8h9lpIPNPdcW9q27sJtgcZD/XlZnJWKQSLQ8t7rc=
# MIGRATOR
MIGRATOR_PORT=3000
GOOSE_DRIVER=postgres
GOOSE_DBSTRING='user=postgres password=HnFxccAF3sdUwnI1EkwmXQ== dbname=postgres sslmode=disable'
GOOSE_MIGRATION_DIR=migrations
# FRONTEND SPA
HTTP=80 # ДЛЯ Certbot
HTTPS=443
+20
View File
@@ -0,0 +1,20 @@
# Нормализовать окончания строк: хранить LF в репозитории
* text=auto
# Явно указать текстовые файлы — Git будет применять конвертацию
*.go text
*.mod text
*.sum text
*.txt text
*.md text
*.json text
*.yml text
*.yaml text
# Бинарные файлы — не трогать окончания строк
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.zip binary
*.exe binary
+41 -13
View File
@@ -1,24 +1,52 @@
name: Deploy
on:
push:
branches:
- main
branches: [main]
paths:
- 'main_dc/**'
jobs:
build-and-deploy:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build Go binary
- name: Deploy
run: |
docker run --rm \
-v ${{ github.workspace }}:/workspace \
-w /workspace/api \
golang:1.22.5 \
sh -c 'go mod tidy && go build -o /workspace/api/bin/api cmd/main.go'
cd /home/gaziz/artefacts/tp/main_dc
git pull origin main
- name: Copy binary and restart service
run: |
cp api/bin/api /home/gaziz/artefacts/tp/main_dc/yalarba/api_yal/bin/api
docker compose -f /home/gaziz/artefacts/tp/main_dc/docker-compose.yml restart api_yal
# Если изменился sites.yml — генерируем конфиги
if git diff --name-only HEAD~1 HEAD | grep -q 'main_dc/sites.yml'; then
echo "→ sites.yml changed, generating configs..."
bash generate-configs.sh
fi
# Авто-детект и пересборка изменённых сервисов
echo "→ Detecting changed services..."
CHANGED=$(git diff --name-only HEAD~1 HEAD | grep -oP 'main_dc/\K[^/]+' | sort -u)
for svc in $CHANGED; do
svc_name="$svc"
# маппинг директорий на имена compose-сервисов
case "$svc" in
BB) svc_name="api_bb" ;;
valitovgaziz) svc_name="valitovgaziz" ;;
nginx|certbot|backup|gitea) svc_name="$svc" ;;
api_bb|api_yal|analytics|db) svc_name="$svc" ;;
yalarba) svc_name="yalarba" ;;
*) svc_name="" ;;
esac
if [ -n "$svc_name" ] && grep -q "^ $svc_name:" docker-compose.yml; then
echo " → Rebuilding $svc_name..."
make stop_$svc_name build_$svc_name start_$svc_name || \
make stop_$svc build_$svc start_$svc 2>/dev/null || \
true
fi
done
# Nginx всегда перезапускаем если изменились конфиги
if echo "$CHANGED" | grep -q 'nginx\|sites.yml'; then
echo " → Reloading nginx..."
docker compose exec -T nginx nginx -s reload 2>/dev/null || \
docker compose restart nginx
fi
+37 -38
View File
@@ -1,38 +1,37 @@
/spa/node_modules
.env
.vscode
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
.DS_Store
dist
dist-ssr
coverage
*.local
/cypress/videos/
/cypress/screenshots/
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
*.tsbuildinfo
# Binaries
api/bin/
/spa/node_modules
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
.DS_Store
coverage
*.local
/cypress/videos/
/cypress/screenshots/
.vscode/*
.vscode/extensions.json
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
*.tsbuildinfo
*.node_modules
/node_modules
/.pnp
.pnp.js
/coverage
/build
.env.local
.env.development.local
.env.test.local
.env.production.local
dist
dist-ssr
!.vscode/extensions.json
.vscode/extensions.json
-16
View File
@@ -1,16 +0,0 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Launch Package",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${fileDirname}",
"args": []
}
]
}
-3
View File
@@ -1,3 +0,0 @@
{
"gigacode.inlineColor": "green"
}
+68 -80
View File
@@ -1,93 +1,81 @@
# YalArba (ЯлАрба) — Tourist Aggregator
# AGENTS.md
**Generated:** 2026-06-12
**Commit:** 165d5a5
**Branch:** main
## Repo overview
## OVERVIEW
Go REST API backend (chi router + GORM + PostgreSQL) with docker-compose orchestration. SPA frontend via Nginx reverse proxy + Certbot. Goose for DB migrations. JWT auth via HttpOnly cookies.
Docker Compose hosting for 4 websites (yalarba.ru, begushiybashkir.ru, easysite102.ru, valitovgaziz.ru).
All infrastructure lives under `main_dc/`. Root `package.json` is vestigial — do not use it.
## Directory structure
## STRUCTURE
```
./
├── api/ # Go backend service
│ ├── cmd/main.go # Entry point
│ ├── src/
│ │ ├── configs/ # Server + DB config structs
│ │ ├── initializers/ # Chi routing + GORM DB init
│ ├── models/ # GORM models (User, Essence, Contact, Point)
│ │ ├── rt/ # Route handlers grouped by domain
│ │ │ ├── auth/ # Login, Register, JWT middleware
│ │ │ ├── admin/ # Admin-only endpoints
│ │ │ ├── prf/ # Profile (stub)
│ │ │ └── srch/ # Search (stub)
│ │ └── storages/psql/ # Global *gorm.DB var
│ └── Dockerfile
├── migrator/ # Goose migration runner
│ └── migrations/ # Timestamped SQL migrations
├── spa/ # Nginx + static HTML landing + certbot
│ ├── index.html # Landing page (Russian)
│ └── data/nginx/ # Nginx config
├── docker-compose.yaml # services: db, api, migrator, spa, certbot
├── Makefile # build, run, clean, test
└── .env # Environment variables (DO NOT COMMIT)
main_dc/
docker-compose.yml -- single compose file orchestrating everything
Makefile -- the primary dev/ops interface; use `make` not raw docker
.env -- shared env: domains, email, api_es port
BB/api_bb/ -- Go REST API (GORM+Chi), port 7777, DB: db_bb (5433)
BB/bbvue/ -- Vue 3 + Vite frontend for begushiybashkir.ru
yalarba/api_tp/ -- Go REST API (GORM+Chi), port 8888, DB: db (5432)
yalarba/api_es/ -- Go REST API (GORM+Chi), port 8088, DB: db (5432)
yalarba/api_yal/ -- Go REST API (GORM+Chi), port 8787, DB: db (5432)
yalarba/easySite/ -- Nuxt 4 SPA for easysite102.ru
yalarba/yalarba-nuxt/ -- Nuxt 4 SPA for yalarba.ru
valitovgaziz/analytics/ -- Node.js (Express) analytics server, port 9999
valitovgaziz/html/ -- static HTML for valitovgaziz.ru
nginx/ -- nginx with automatic HTTP↔HTTPS switching
certbot/ -- Let's Encrypt cert management
stubSite/ -- placeholder site while building
```
## WHERE TO LOOK
| Task | Location | Notes |
|------|----------|-------|
| Add API route | `api/src/initializers/Routing.go` + `api/src/rt/` | Register handler in Routing.go; create handler in rt/ subdir |
| Add DB model | `api/src/models/` | GORM struct with json + gorm tags; UUID PKs |
| Add migration | `migrator/migrations/` | Goose format: timestamp_description.sql |
| Auth logic | `api/src/rt/auth/` | JWT in HttpOnly cookie; claims in request context |
| DB access | `api/src/storages/psql/psql.go` | Global `PSQL_GORM_DB *gorm.DB` |
| Config/env vars | `.env``os.Getenv()` in code | Config structs in `api/src/configs/` |
## Developer commands (always run from `main_dc/`)
## CODE MAP
| Symbol | Type | Location | Role |
|--------|------|----------|------|
| `main` | func | `api/cmd/main.go:16` | Entry: calls InitChiRouting + InitDBconnection |
| `InitChiRouting` | func | `api/src/initializers/Routing.go:21` | Sets up chi router, middlewares, all routes |
| `InitDBconnection` | func | `api/src/initializers/PGQL_DB.go:14` | GORM Postgres connection, sets psql.PSQL_GORM_DB |
| `PSQL_GORM_DB` | var | `api/src/storages/psql/psql.go:5` | Global DB handle |
| `Login` | func | `api/src/rt/auth/Login.go:21` | Validates creds, issues JWT cookie |
| `Register` | func | `api/src/rt/auth/Registr.go:54` | Validates input, hashes password, creates user |
| `AuthMiddleware` | func | `api/src/rt/auth/authMiddleware.go:11` | JWT cookie validation middleware |
| `AuthAdminMiddleware` | func | `api/src/rt/auth/authAdminMiddlware.go:11` | JWT + role=admin check |
| `User` | struct | `api/src/models/user.go:5` | GORM model: id, name, email, password, phone, role |
| `Claims` | struct | `api/src/models/authDataStructs.go:15` | JWT claims: email, phone, role |
| Command | What it does |
|---|---|
| `make all` | Full cycle: down → git pull → build --no-cache → up -d → watch |
| `make <svc>` | Full cycle for one service, e.g. `make api_bb`, `make nginx`, `make es`, `make analytics` |
| `make bbvue` | Rebuild Vue frontend (calls `npm run build` in `BB/bbvue/`) |
| `make vue_bb` | git pull + npm cache clean + bbvue build + watch |
| `make wn` | `watch -n2 docker ps` — monitor containers |
| `make bb_db` | `psql -U postgres -d bb_db` inside db_bb container |
## CONVENTIONS
- **Naming**: Go package names are short abbreviations (auth, prf, srch, admin). File names in PascalCase.
- **DB**: GORM with UUID PKs (`AutoIncrement:false`). Goose for migrations with timestamp prefixes.
- **Auth**: JWT stored in HttpOnly/Secure/SameSite cookie. Role stored in claims. Context key is `"email"`.
- **Config**: All config via `os.Getenv()`, no config files. `.env` loaded by docker-compose.
- **Logging**: `log/slog` structured logging.
- **Errors**: `http.Error()` for simple errors. JSON-encoded `validationError` struct for validation.
- **Password**: bcrypt cost 14. Max password length 72 (bcrypt limit).
- **Role defaults**: Empty role on registration → "user".
All `build_*` targets use `--no-cache`.
All full-cycle targets follow: `stop_<svc> → git → build_<svc> → start_<svc> → wn`.
## ANTI-PATTERNS (THIS PROJECT)
- `os.Exit(2)` on DB failure — kills entire process, no graceful shutdown
- Duplicate JWT parsing logic between `authMiddleware.go` and `authAdminMiddlware.go`
- `authAdminMiddlware.go` has typo in filename ("Middlware")
- `AuthUserMiddleware.go` in prf/ is a dead stub (always 401)
- Global mutable state (`PSQL_GORM_DB`, package-level `jwtKey`)
- HTTPS enforced on JWT cookies but HTTP port 80 exposed — mismatch in dev
- `AGENTS.md` in `.gitignore` because it previously contained secrets — credentials belong in `.env` only
## Frontend dev (outside compose)
## COMMANDS
```bash
make # docker compose up (default)
make build # docker compose build
make run # docker compose up
make clean # docker builder prune
make test # go test ./api/src/auth/... -v
make tc # go test -cover
cd main_dc/BB/bbvue && npm run dev # Vite dev server
cd main_dc/BB/bbvue && npm run lint # ESLint --fix
cd main_dc/BB/bbvue && npm run format # Prettier --write src/
# serv_spa удалён — yalarba работает через yalarba-nuxt (Nuxt SSR)
cd main_dc/yalarba/easySite && npm run dev # Nuxt dev
cd main_dc/yalarba/easySite && npm run build # Nuxt build
```
## NOTES
- `.env` contains real secrets — never commit. The `.gitignore` entry for `AGENTS.md` was added because it previously held credentials. Remove that `.gitignore` line once this file is clean.
- `Profile` and `Search` handlers are stubs (empty bodies).
- Docker services depend on each other: db → api → migrator → spa+certbot.
- Goose migration `GOOSE_DBSTRING` in `.env` must match PG credentials.
## Service quirks
- **Nginx SSL**: `switch-config.sh` is all-or-nothing — HTTPS only activates when *every* domain has a cert. Until then, SSL port redirects back to HTTP.
- **`yalarba/serv_spa/`**: удалён — был legacy Vue SPA, не использовался.
- **`api_yal`** is the only container that runs as non-root. Runs on port 8787.
- **`api_es`** port is configurable via `API_ES_APP_PORT` in `.env` (default 8088). All other API ports are hardcoded.
- **Databases**: `db` (port 5432) is shared between api_tp, api_es, api_yal. `db_bb` (port 5433) is dedicated to api_bb.
- **GORM auto-migration**: All Go APIs use GORM auto-migrate at startup — no manual migration tooling.
- **Keycloak** referenced in Makefile targets but absent from docker-compose.yml — likely not deployed.
- **`api_yal/testrunner`**: standalone Go test runner binary (not containerized), for running integration test suites.
## Docs convention
READMEs and documentation are primarily in Russian. See `documentation/` for Makefile, Docker, restart, and LLM info docs.
## Server (YalArbaServer)
| Field | Value |
|---|---|
| IP | `94.41.23.97` |
| User | `gaziz` |
| SSH key | `~/.ssh/id_ed25519` (local) |
| SSH | `ssh gaziz@94.41.23.97` |
| Root password | `sudoowneranduser` |
| User `gaziz` password | `sudoowneranduser` |
| Repo path | `/home/gaziz/artefacts/tp/main_dc` |
-16
View File
@@ -1,16 +0,0 @@
build:
@docker compose build
run:
@docker compose up
clean:
@docker builder prune
test:
@go test ./api/src/auth/... -v
tc:
@go test -cover
.DEFAULT_GOAL=run
+39 -3
View File
@@ -1,5 +1,41 @@
### Microservices on docker-compose.
# Hosting by ValitovGaziz's team on docker compose
## before start your need to set .env file with your VARIABLES
## for yalarba.ru && begushiybashkir.ru
## build and start with command: make
В этом репозитроии собранны все сервисы для работы приложений YalArba. Тае же есть отдельный сайт для ValitovGaziz.ru && BegushiyBashkir.ru. Будет много дополнений и развития поэтому буду стараться поддерживать документацию в валидном состоянии.
### BackEnd api_bb
REST API on golang. Frameworks gorm with PostgresQL. Migration on automigrate with gorm into REST API server.
### FrontEnd vue_bb
Vue3.js, pinia, axios.
### product owner Zagir Загир тренер FOR
### BackEnd api_es
EasySite102.ru REST API on Golang. Frameworks gorm with PostgresQL, automigraion with gorm and Chi rounting.
### FrontEnd nuxt_es
SPA on nuxt.js (vue3.js, axios, pinia).
### BackEnd api_ya
yalarba.ru/api/ REST API on Golang. Frameworks gorm with PostgresQL, automigraion with gorm and Chi rounting.
### FrontEnd vue_ya
yalarba.ru on vue3.js (pinia) need to redevelop on nuxt.js
Ближайшие задачи
!!! Need documentation for working REST API and working SPA aps
1. Написать документацию к api всех сайтов
2. Доработать begushiybashkir.ru && easysite102.rr
# документация находится в директории documentation в корне проекта
-11
View File
@@ -1,11 +0,0 @@
FROM golang:1.22.5
WORKDIR /app
COPY . .
RUN go mod tidy
RUN go build -o bin/api cmd/main.go
ENTRYPOINT [ "bin/api" ]
-2
View File
@@ -1,2 +0,0 @@
t:
@go test ./... -v
-22
View File
@@ -1,22 +0,0 @@
package main
import (
"api/src/configs"
"api/src/initializers"
"log/slog"
"os"
)
// TODO write the tests
var APIServerCnf configs.APIserver
var PSQLCnf configs.PSQLConfig
var SecretKey = []byte(os.Getenv("SECRET_KEY"))
func main() {
slog.Info("Start")
initializers.InitChiRouting()
initializers.InitDBconnection()
slog.Info("server is closed", "info", <-initializers.Done)
slog.Info("End")
}
-32
View File
@@ -1,32 +0,0 @@
module api
go 1.26.0
require github.com/go-chi/chi/v5 v5.1.0
require (
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/pgx/v5 v5.6.0 // indirect
github.com/jackc/puddle/v2 v2.2.1 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/stretchr/testify v1.9.0 // indirect
golang.org/x/crypto v0.25.0
golang.org/x/sync v0.12.0 // indirect
golang.org/x/text v0.16.0 // indirect
)
require (
github.com/golang-jwt/jwt/v4 v4.5.0
github.com/google/uuid v1.6.0
gorm.io/driver/postgres v1.5.9
gorm.io/gorm v1.25.11
)
require (
github.com/go-chi/httprate v0.15.0 // indirect
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
github.com/zeebo/xxh3 v1.0.2 // indirect
golang.org/x/sys v0.30.0 // indirect
)
-52
View File
@@ -1,52 +0,0 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw=
github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/httprate v0.15.0 h1:j54xcWV9KGmPf/X4H32/aTH+wBlrvxL7P+SdnRqxh5g=
github.com/go-chi/httprate v0.15.0/go.mod h1:rzGHhVrsBn3IMLYDOZQsSU4fJNWcjui4fWKJcCId1R4=
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY=
github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw=
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0=
github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA=
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/postgres v1.5.9 h1:DkegyItji119OlcaLjqN11kHoUgZ/j13E0jkJZgD6A8=
gorm.io/driver/postgres v1.5.9/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI=
gorm.io/gorm v1.25.11 h1:/Wfyg1B/je1hnDx3sMkX+gAlxrlZpn6X0BXRlwXlvHg=
gorm.io/gorm v1.25.11/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
-9
View File
@@ -1,9 +0,0 @@
package configs
import(
)
type APIserver struct {
Server_port string
}
-9
View File
@@ -1,9 +0,0 @@
package configs
type PSQLConfig struct {
Db_user string
Db_password string
Db_name string
Db_port string
Host_db string
}
-43
View File
@@ -1,43 +0,0 @@
package initializers
import (
"api/src/storages/psql"
"fmt"
"log/slog"
"os"
"gorm.io/driver/postgres"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)
func InitDBconnection() {
slog.Info("Init DB connection")
dsn := fmt.Sprintf(
"host=%s user=%s password=%s dbname=%s port=%s sslmode=disable TimeZone=Asia/Yekaterinburg",
os.Getenv("PGHOST"),
os.Getenv("PGUSER"),
os.Getenv("PGPASSWORD"),
os.Getenv("PGDATABASE"),
os.Getenv("PGPORT"),
)
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{
Logger: logger.Default.LogMode(logger.Info),
})
if err != nil {
slog.Error("failed to connect database", "error", err)
os.Exit(2)
}
psql.PSQL_GORM_DB = db
sql, err := db.DB()
if err != nil {
slog.Error("failed to get database", "error", err)
os.Exit(2)
}
err = sql.Ping()
if err != nil {
slog.Error("failed to ping database", "error", err)
os.Exit(2)
}
slog.Info("connected to database")
}
-68
View File
@@ -1,68 +0,0 @@
package initializers
import (
"api/src/rt/admin"
"api/src/rt/auth"
"api/src/rt/prf"
"api/src/rt/srch"
"log/slog"
"os"
"time"
"net/http"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"github.com/go-chi/httprate"
)
var Done = make(chan bool)
func InitChiRouting() {
slog.Info("Init routing")
r := chi.NewRouter()
// middlewares
r.Use(middleware.Logger)
r.Use(middleware.Timeout(60 * time.Second))
r.Use(middleware.RequestID)
r.Use(middleware.CleanPath)
r.Use(middleware.Heartbeat("/ping"))
r.Use(middleware.NoCache)
r.Use(middleware.Recoverer)
r.NotFound(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(404)
w.Write([]byte("route does not exist"))
})
r.MethodNotAllowed(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(405)
w.Write([]byte("method is not valid"))
})
// public Routes
r.Group(func(r chi.Router) {
r.Post("/signup", auth.Register) // register
r.With(httprate.Limit(5, 1*time.Minute)).Post("/signin", auth.Login) // signin with rate limiter
r.Get("/search", srch.Search)
})
// Private Routes
// Require Authentication
r.Group(func(r chi.Router) {
r.Use(auth.AuthMiddleware)
r.Get("/profile", prf.Profile)
r.Route("/admin", func(r chi.Router) {
r.Use(auth.AuthAdminMiddleware)
r.Get("/allUsersAdm", admin.GetAllUser) // all users get
})
})
// up server on os.Getenv("SERVER_PORT") port on gorutin
go func() {
defer close(Done)
err := http.ListenAndServe(":"+os.Getenv("SERVER_PORT"), r)
if err != nil {
slog.Error("Can't start server: ", "error", err)
}
}()
}
-11
View File
@@ -1,11 +0,0 @@
package models
import "github.com/google/uuid"
type Contact struct {
Id uuid.UUID `json:"id" gorm:"type:uuid;primaryKey;unique;AutoIncrement:false"`
Email string `json:"email" gorm:"type:string"`
Phone string `json:"phone" gorm:"type:string"`
Address string `json:"address" gorm:"type:string"`
Point Point `json:"point" gorm:"type:struct"`
}
-13
View File
@@ -1,13 +0,0 @@
package models
import "github.com/google/uuid"
type Essence struct {
Id uuid.UUID `json:"id" gorm:"type:uuid;primaryKey;unique;AutoIncrement:false"`
Name string `json:"name" gorm:"type:string"`
Type string `json:"type" gorm:"type:string"`
Contact Contact `json:"contact" gorm:"type:struct"`
ShortDescription string `json:"shortDesc" gorm:"type:string"`
Description string `json:"description" gorm:"type:string"`
AverageBill int `json:"number" gorm:"type:int"`
}
-9
View File
@@ -1,9 +0,0 @@
package models
import "github.com/google/uuid"
type Point struct {
Id uuid.UUID `json:"id" gorm:"type:uuid;primaryKey;unique;AutoIncrement:false"`
Latitude int64 `json:"latitude" gorm:"type:int64"`
Longitude int64 `json:"longitude" gorm:"type:int64"`
}
-20
View File
@@ -1,20 +0,0 @@
package models
import (
"github.com/golang-jwt/jwt/v4"
)
type Credentials struct {
Name string `json:"name"`
Email string `json:"email"`
Password string `json:"password"`
Phone string `json:"phone"`
Role string `json:"role"`
}
type Claims struct {
jwt.RegisteredClaims
Email string `json:"email"`
Phone string `json:"phone"`
Role string `json:"role"`
}
-12
View File
@@ -1,12 +0,0 @@
package models
import "github.com/google/uuid"
type User struct {
Id uuid.UUID `json:"id" gorm:"type:uuid;primaryKey;unique;AutoIncrement:false"`
Name string `json:"name" gorm:"type:string"`
Email string `json:"email" gorm:"type:string;index"`
Password string `json:"password" gorm:"type:string;index"`
Phone string `json:"phone" gorm:"type:string;index"`
Role string `json:"role" gorm:"type:string;index"`
}
-27
View File
@@ -1,27 +0,0 @@
package admin
import (
"api/src/models"
"api/src/storages/psql"
"encoding/json"
"net/http"
)
func GetAllUser(w http.ResponseWriter, r *http.Request) {
var users []models.User
qr := psql.PSQL_GORM_DB.Find(&users)
if qr.Error != nil {
w.WriteHeader(http.StatusNotFound)
return
}
jsData, err := json.Marshal(users)
if err != nil {
w.WriteHeader(http.StatusNotAcceptable)
return
}
w.Write([]byte(jsData))
}
-68
View File
@@ -1,68 +0,0 @@
package auth
import (
"api/src/models"
"api/src/storages/psql"
"encoding/json"
"net/http"
"os"
"time"
"github.com/golang-jwt/jwt/v4"
"golang.org/x/crypto/bcrypt"
)
const (
loginErrMsg = "invalid email or password"
)
var jwtKey = []byte(os.Getenv("SECRET_KEY"))
func Login(w http.ResponseWriter, r *http.Request) {
var creds models.Credentials
if err := json.NewDecoder(r.Body).Decode(&creds); err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}
// check user
var user models.User
result := psql.PSQL_GORM_DB.Where("email = ?", creds.Email).First(&user)
if result.Error != nil || !checkPasswordHash(creds.Password, user.Password) {
http.Error(w, loginErrMsg, http.StatusUnauthorized)
return
}
// create jwt token
expirationtime := time.Now().Add(5 * time.Minute)
claims := &models.Claims{
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(expirationtime),
},
Email: user.Email,
Phone: user.Phone,
Role: user.Role,
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
tokenString, err := token.SignedString(jwtKey)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
http.SetCookie(w, &http.Cookie{
Name: "token",
Value: tokenString,
Expires: expirationtime,
HttpOnly: true,
Secure: true,
SameSite: http.SameSiteStrictMode,
Path: "/",
})
w.WriteHeader(http.StatusOK)
}
func checkPasswordHash(password, hash string) bool {
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
return err == nil
}
-97
View File
@@ -1,97 +0,0 @@
package auth
import (
"api/src/models"
"api/src/storages/psql"
"encoding/json"
"net/http"
"regexp"
"strings"
"github.com/google/uuid"
"golang.org/x/crypto/bcrypt"
)
var (
emailRegex = regexp.MustCompile(`^[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}$`)
phoneRegex = regexp.MustCompile(`^\+?[0-9\s\-\(\)]{7,20}$`)
validRoles = map[string]bool{"user": true, "admin": true}
)
type validationError struct {
Field string `json:"field"`
Message string `json:"message"`
}
func validateCredentials(c *models.Credentials) []validationError {
var errs []validationError
c.Name = strings.TrimSpace(c.Name)
c.Email = strings.TrimSpace(c.Email)
c.Phone = strings.TrimSpace(c.Phone)
c.Role = strings.TrimSpace(c.Role)
if c.Name == "" || len(c.Name) > 50 {
errs = append(errs, validationError{"name", "name is required and must be at most 50 characters"})
}
if c.Email == "" || len(c.Email) > 50 || !emailRegex.MatchString(c.Email) {
errs = append(errs, validationError{"email", "valid email is required"})
}
if c.Password == "" || len(c.Password) < 8 || len(c.Password) > 72 {
errs = append(errs, validationError{"password", "password must be between 8 and 72 characters"})
}
if c.Phone == "" || !phoneRegex.MatchString(c.Phone) {
errs = append(errs, validationError{"phone", "valid phone number is required"})
}
if c.Role != "" && !validRoles[c.Role] {
errs = append(errs, validationError{"role", "role must be 'user' or 'admin'"})
}
return errs
}
func Register(w http.ResponseWriter, r *http.Request) {
var creds models.Credentials
if err := json.NewDecoder(r.Body).Decode(&creds); err != nil {
http.Error(w, "invalid request body", http.StatusBadRequest)
return
}
if errs := validateCredentials(&creds); errs != nil {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(map[string]interface{}{"errors": errs})
return
}
hashedPassword, err := hashPassword(creds.Password)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
if creds.Role == "" {
creds.Role = "user"
}
user := models.User{
Id: uuid.New(),
Name: creds.Name,
Email: creds.Email,
Password: hashedPassword,
Phone: creds.Phone,
Role: creds.Role,
}
result := psql.PSQL_GORM_DB.Create(&user)
if result.Error != nil {
http.Error(w, "user with this email or phone already exists", http.StatusConflict)
return
}
w.WriteHeader(http.StatusCreated)
}
func hashPassword(password string) (string, error) {
bytes, err := bcrypt.GenerateFromPassword([]byte(password), 14)
return string(bytes), err
}
-50
View File
@@ -1,50 +0,0 @@
package auth
import (
"api/src/models"
"context"
"net/http"
"github.com/golang-jwt/jwt/v4"
)
func AuthAdminMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
c, err := r.Cookie("token")
if err != nil {
if err == http.ErrNoCookie {
w.WriteHeader(http.StatusUnauthorized)
return
}
w.WriteHeader(http.StatusBadRequest)
return
}
tknStr := c.Value
claims := &models.Claims{}
tkn, err := jwt.ParseWithClaims(tknStr, claims, func(token *jwt.Token) (interface{}, error) {
return jwtKey, nil
})
if err != nil {
if err == jwt.ErrSignatureInvalid {
w.WriteHeader(http.StatusUnauthorized)
return
}
w.WriteHeader(http.StatusBadRequest)
return
}
if !tkn.Valid {
w.WriteHeader(http.StatusUnauthorized)
return
}
if claims.Role != "admin" {
w.WriteHeader(http.StatusNonAuthoritativeInfo)
return
}
ctx := context.WithValue(r.Context(), "email", claims.Email)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
-45
View File
@@ -1,45 +0,0 @@
package auth
import (
"api/src/models"
"context"
"net/http"
"github.com/golang-jwt/jwt/v4"
)
func AuthMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
c, err := r.Cookie("token")
if err != nil {
if err == http.ErrNoCookie {
w.WriteHeader(http.StatusUnauthorized)
return
}
w.WriteHeader(http.StatusBadRequest)
return
}
tknStr := c.Value
claims := &models.Claims{}
tkn, err := jwt.ParseWithClaims(tknStr, claims, func(token *jwt.Token) (interface{}, error) {
return jwtKey, nil
})
if err != nil {
if err == jwt.ErrSignatureInvalid {
w.WriteHeader(http.StatusUnauthorized)
return
}
w.WriteHeader(http.StatusBadRequest)
return
}
if !tkn.Valid {
w.WriteHeader(http.StatusUnauthorized)
return
}
ctx := context.WithValue(r.Context(), "email", claims.Email)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
-16
View File
@@ -1,16 +0,0 @@
package auth
import (
"testing"
"golang.org/x/crypto/bcrypt"
)
func Test_hashPass(t *testing.T) {
password := "some hard password"
hash, _ := hashPassword(password)
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
if err != nil {
t.Errorf("Falis by: %s", err)
}
}
-9
View File
@@ -1,9 +0,0 @@
package prf
import "net/http"
func AuthUserMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusUnauthorized)
})
}
-7
View File
@@ -1,7 +0,0 @@
package prf
import "net/http"
func Profile(w http.ResponseWriter, r *http.Request) {
}
-7
View File
@@ -1,7 +0,0 @@
package srch
import "net/http"
func Search(w http.ResponseWriter, r *http.Request) {
}
-5
View File
@@ -1,5 +0,0 @@
package psql
import "gorm.io/gorm"
var PSQL_GORM_DB *gorm.DB
-71
View File
@@ -1,71 +0,0 @@
services:
db:
image: postgres:16
env_file:
- .env
ports:
- "${PGPORT}:${PGPORT}"
volumes:
- postgres-db:/var/lib/postgresql/data
environment:
- POSTGRES_USER=${PGUSER}
- POSTGRES_PASSWORD=${PGPASSWORD}
- POSTGRES_DB=${PGDATABASE}
api:
build:
context: ./api
dockerfile: Dockerfile
env_file:
- .env
ports:
- "${SERVER_PORT}:${SERVER_PORT}"
volumes:
- api:/usr/src/app
depends_on:
- db
command: ./bin/api
migrator:
build:
context: ./migrator
dockerfile: Dockerfile
env_file:
- .env
depends_on:
- api
- db
volumes:
- goose:/migrations
command: goose up
spa:
build: .
env_file:
- .env
ports:
- "${HTTP}:${HTTP}"
- "${HTTPS}:${HTTPS}"
volumes:
- ./data/nginx/conf.d:/etc/nginx/conf.d
- ./data/certbot/conf:/etc/letsencrypt
- ./data/certbot/www:/var/www/certbot
depends_on:
- api
- db
- migrator
- certbot
certbot:
image: certbot/certbot
volumes:
- ./data/certbot/conf:/etc/letsencrypt
- ./data/certbot/www:/var/www/certbot
entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"
restart: unless-stopped
volumes:
api:
postgres-db:
goose:
+103
View File
@@ -0,0 +1,103 @@
# Документация по Docker Compose проекту
## Обзор проекта
Данный проект представляет собой комплексную инфраструктуру для хостинга нескольких веб-сайтов и сервисов с использованием Docker Compose.
## Сервисы
### 1. Certbot
**Назначение**: Автоматическое получение и обновление SSL/TLS сертификатов Let's Encrypt
- **Контейнер**: `certbot`
- **Порты**: Нет внешних портов
- **Тома**:
- `certbot_data` - данные сертификатов
- `certbot_www` - веб-корень для проверки доменов
- **Зависимости**: Требует настройки доменов в .env файле
### 2. Nginx
**Назначение**: Веб-сервер и обратный прокси для всех сайтов
- **Контейнер**: `nginx`
- **Порты**: `80:80`, `443:443`
- **Тома**: Статические файлы всех сайтов + сертификаты Certbot
- **Зависимости**: Все остальные сервисы должны быть здоровы
- **Сети**: `web-network`, `internal`, `app-network`, `bb-network`
### 3. Analytics
**Назначение**: Система статистики для сайта valitovgaziz.ru
- **Контейнер**: `analytics`
- **Порты**: `9999:3000`
- **Технология**: Node.js 22+
- **Тома**: Логи и данные аналитики
- **Сети**: `web-network`, `internal`
### 4. API для Yalarba.ru (api_tp)
**Назначение**: REST API для бизнес-логики yalarba.ru
- **Контейнер**: `api_tp`
- **Порты**: `8888:8080`
- **Технология**: Golang (Gorm, Chi)
- **База данных**: `db` (PostgreSQL)
- **Сети**: `app-network`
### 5. Основная база данных (db)
**Назначение**: База данных для yalarba.ru и easysite102.ru
- **Контейнер**: `db_tp`
- **Порты**: `5432:5432`
- **Технология**: PostgreSQL 15
- **Тома**: `db_tp_data`
- **Миграции**: ./migrations
- **Сети**: `app-network`
### 6. API для Бегущий Башкир (api_bb)
**Назначение**: REST API для сайта Бегущий Башкир
- **Контейнер**: `api_bb`
- **Порты**: `7777:8080`
- **Технология**: Golang (Gorm, Chi)
- **База данных**: `db_bb` (PostgreSQL)
- **Тома**: Загружаемые файлы
- **Сети**: `bb-network`
### 7. База данных Бегущий Башкир (db_bb)
**Назначение**: База данных для сайта Бегущий Башкир
- **Контейнер**: `db_bb`
- **Порты**: `5433:5432`
- **Технология**: PostgreSQL 15
- **Тома**: `db_bb_data`
- **Сети**: `bb-network`
### 8. Easysite SPA
**Назначение**: Интерфейс для туристического бизнеса
- **Контейнер**: `easysite`
- **Порты**: `3000:3000`
- **Технология**: Nuxt.js
- **Сети**: `web-network`, `app-network`
### 9. API для Easysite (api_es)
**Назначение**: REST API для easysite102.ru
- **Контейнер**: `api_es`
- **Порты**: Определяется через переменную окружения
- **База данных**: `db` (общая с yalarba.ru)
- **Сети**: `app-network`, `web-network`
## Сети
- **web-network**: Для веб-сервисов, доступных извне
- **internal**: Для внутренних сервисов
- **app-network**: Для приложений yalarba.ru
- **bb-network**: Для приложений Бегущий Башкир
## Тома
- `certbot_data`, `certbot_www` - данные Certbot
- `db_tp_data`, `db_bb_data` - данные баз данных
- `api_bb_uploads` - загружаемые файлы
- `analytics_logs`, `analytics_data` - логи и данные аналитики
## Переменные окружения
Создайте файл `.env` в корне проекта со следующими переменными:
```env
EMAIL=your-email@example.com
ALL_DOMAINS=domain1.ru,domain2.ru
API_ES_APP_PORT=8088
+53
View File
@@ -0,0 +1,53 @@
# LLM Information
## Current LLM Configuration
Based on system analysis conducted on 2026-04-16, the following LLM (Large Language Model) is being used:
### Model Details
- **Model Name**: `sourcecraft_model`
- **Current Mode**: Architect (`architect`)
- **Mode Display Name**: 🏗️ Architect
- **System**: SourceCraft Code Assistant Agent
### Environment Context
- **Operating System**: Windows 11
- **Default Shell**: C:\WINDOWS\system32\cmd.exe
- **Workspace Directory**: d:/artifacts/tp
- **User Time Zone**: Asia/Yekaterinburg (UTC+5:00)
### Capabilities
The SourceCraft Code Assistant Agent is an experienced technical leader with capabilities including:
- Information gathering and context analysis
- Detailed planning and task breakdown
- Code writing and modification
- System operations and command execution
- File management and editing
- Web development and debugging
### Modes Available
The system supports multiple specialized modes:
1. **🏗️ Architect** (current) - Planning, design, and strategy
2. **💻 Code** - Code writing, modification, and refactoring
3. **❓ Ask** - Explanations, documentation, and technical questions
4. **🪲 Debug** - Troubleshooting and error diagnosis
5. **🪃 Orchestrator** - Complex multi-step project coordination
### Project Context
The current workspace contains a Docker-based hosting solution for multiple websites:
- yalarba.ru
- begushiybashkir.ru
- easysite102.ru
- valitovgaziz.ru
The project includes backend APIs in Go, frontend applications in Vue.js/Nuxt.js, and various supporting services (nginx, certbot).
### Analysis Method
This information was gathered through:
1. System environment details inspection
2. File structure analysis
3. Configuration file review (package.json, README.md)
4. Current mode and model identification from system metadata
### Last Updated
2026-04-16T15:25:15.218Z
+119
View File
@@ -0,0 +1,119 @@
# Документация к Makefile
Этот Makefile содержит набор команд для управления Docker-контейнерами в проекте, включая сборку, запуск, остановку и мониторинг различных сервисов.
## Основные команды
### Управление всеми сервисами
- `all` - Полный цикл обновления API bb: обновление кода, остановка, сборка, запуск и просмотр логов
- `restart_all` - Перезапуск всех контейнеров и запуск мониторинга
- `stop_all` - Остановка всех контейнеров
- `build_all` - Полная пересборка всех контейнеров (без кеша)
- `start_all` - Запуск всех контейнеров в фоновом режиме
- `restart` - Полный перезапуск: остановка, обновление кода, сборка и запуск
### Управление API bb
- `api_bb` - Полный цикл обновления API bb
- `stop_bb` - Остановка контейнера api_bb
- `build_bb` - Пересборка контейнера api_bb
- `run_bb` - Запуск контейнера api_bb в фоновом режиме
- `api_bb_logs` - Просмотр логов контейнера api_bb в реальном времени
### Управление базой данных
- `bb_db` - Подключение к базе данных bb_db в контейнере PostgreSQL
### Управление фронтендом (Vue.js)
- `npm_clean` - Очистка кеша npm
- `bbvue` - Сборка Vue.js приложения
- `vue_bb` - Полный цикл обновления фронтенда: обновление кода, очистка кеша, сборка и мониторинг
### Управление nginx
- `nginx` - Полный цикл обновления nginx
- `stop_nginx` - Остановка контейнера nginx
- `build_nginx` - Пересборка контейнера nginx
- `start_nginx` - Запуск контейнера nginx
- `logs_nginx` - Просмотр логов nginx
### Управление Keycloak
- `keycloak` - Полный цикл обновления Keycloak
- `re_kk` - Быстрый перезапуск Keycloak (без пересборки)
- `stop_kk` - Остановка контейнера keycloak
- `build_kk` - Пересборка контейнера keycloak
- `start_kk` - Запуск контейнера keycloak
- `logs_kk` - Просмотр логов keycloak
### Управление Easysite
- `es` - Полный цикл обновления Easysite
- `easysite_stop` - Остановка контейнера easysite
- `easysite_build` - Пересборка контейнера easysite
- `easysite_start` - Запуск контейнера easysite с проверкой статуса
- `easysite_logs` - Просмотр логов easysite
- `build_es_log` - Сборка easysite с записью логов в файл
- `build_es_log_all` - Детальная сборка easysite с подробным выводом
### Управление Analytics
- `analytics` - Полный цикл обновления analytics
- `stop_analitics` - Остановка контейнера analytics
- `build_analititcs` - Пересборка контейнера analytics
- `start_analytics` - Запуск контейнера analytics с проверкой статуса
- `restart_analytics` - Быстрый перезапуск analytics
### Управление API Easysite
- `api_es` - Полный цикл обновления api_es
- `stop_api_es` - Остановка контейнера api_es
- `build_api_es` - Пересборка контейнера api_es
- `start_api_es` - Запуск контейнера api_es
### Управление Certbot
- `certbot` - Полный цикл обновления certbot
- `stop_cerbot` - Остановка контейнера certbot
- `build_certbot` - Пересборка контейнера certbot
- `start_certbot` - Запуск контейнера certbot
### Управление API TP
- `api_tp` - Полный цикл обновления api_tp
- `stop_api_tp` - Остановка контейнера api_tp
- `build_api_tp` - Пересборка контейнера api_tp
- `start_api_tp` - Запуск контейнера api_tp
### Вспомогательные команды
- `git` - Обновление кода из репозитория
- `wn` - Мониторинг состояния контейнеров (обновление каждые 2 секунды)
- `top` - Запуск htop для мониторинга системы
## Паттерны использования
### Стандартный цикл обновления сервиса:
```bash
make stop_<service> git build_<service> start_<service> wn
```
### Быстрый перезапуск:
```bash
make restart_<service>
```
### Просмотр логов:
```bash
make <service>_logs
```
## Примечания
- Все команды сборки используют `--no-cache` для обеспечения чистой сборки
- Большинство команд включают автоматическое обновление кода (`git pull`)
- Команда `wn` предоставляет удобный мониторинг состояния контейнеров
- Проект использует Docker Compose для оркестрации контейнеров
- В проекте присутствуют различные сервисы: API, базы данных, фронтенд, аутентификация, аналитика
+103
View File
@@ -0,0 +1,103 @@
# Автоматический запуск микросервисов после перезагрузки сервера
## Debuan OS server version
## Обзор системы
Данная система состоит из нескольких микросервисов, развернутых через Docker Compose. Для обеспечения автоматического запуска после перезагрузки сервера настроена интеграция с systemd.
## Конфигурация автоматического запуска
### 1. Systemd Service для Docker
В системе настроен сервис Docker для автоматического запуска при загрузке ОС:
```bash
sudo systemctl enable docker.service
```
Эта команда гарантирует, что Docker демон будет автоматически запускаться при старте системы.
### 2. Docker Compose и автоматический перезапуск
В файле `docker-compose.yml` для каждого сервиса настроена политика перезапуска:
```yaml
restart: unless-stopped
```
Эта политика означает:
- Сервисы автоматически перезапускаются при выходе из строя
- Сервисы не перезапускаются, если были остановлены вручную
- При перезагрузке системы все сервисы автоматически запускаются
## Процесс запуска после перезагрузки
### Последовательность запуска:
1. **Загрузка ОС** → systemd запускает Docker демон
2. **Docker** → автоматически восстанавливает контейнеры с политикой `unless-stopped`
3. **Health checks** → система проверяет готовность каждого сервиса
4. **Зависимости** → сервисы запускаются в правильном порядке согласно `depends_on`
### Health Checks и зависимости
Система использует health checks для контроля готовности сервисов:
- **Базы данных**: проверка доступности через `pg_isready`
- **API сервисы**: HTTP health checks на эндпоинты `/health`
- **Nginx**: проверка через `curl http://localhost/health`
- **Certbot**: проверка наличия SSL сертификатов
## Мониторинг состояния системы
После перезагрузки проверьте статус системы:
```bash
# Проверить статус всех контейнеров
docker-compose ps
# Просмотреть логи запуска
docker-compose logs
# Проверить health status
docker ps --format "table {{.Names}}\t{{.Status}}"
```
## Ручное управление сервисами
```bash
# Остановить все сервисы
docker-compose down
# Запустить все сервисы
docker-compose up -d
# Перезапустить конкретный сервис
docker-compose restart nginx
```
## Важные замечания
1. **Время запуска**: Полный запуск системы может занять 2-5 минут из-за health checks и зависимостей
2. **Порядок запуска**: Критические сервисы (БД) запускаются первыми
3. **Volume данные**: Данные сохраняются между перезагрузками благодаря Docker volumes
4. **Сетевые соединения**: Сети восстанавливаются автоматически
## Устранение неисправностей
Если сервисы не запускаются после перезагрузки:
```bash
# Проверить статус Docker
sudo systemctl status docker
# Проверить логи Docker
sudo journalctl -u docker.service
# Принудительно перезапустить композ
docker-compose down
docker-compose up -d
```
Система спроектирована для полного самовосстановления после перезагрузки сервера без необходимости ручного вмешательства.
+13
View File
@@ -0,0 +1,13 @@
EMAIL=valitovgaziz@yandex.ru
#CERTBOT NGINX VARIABLES — авто-сгенерировано, не редактировать вручную
ALL_DOMAINS=yalarba.ru,www.yalarba.ru,valitovgaziz.ru,www.valitovgaziz.ru,easysite102.ru,www.easysite102.ru,begushiybashkir.ru,www.begushiybashkir.ru,xn--80abahjtcfl5d0a8di.xn--p1ai,www.xn--80abahjtcfl5d0a8di.xn--p1ai
DOMAINS_begushiybashkir=begushiybashkir.ru,www.begushiybashkir.ru
DOMAINS_begushiybashkir_idn=xn--80abahjtcfl5d0a8di.xn--p1ai,www.xn--80abahjtcfl5d0a8di.xn--p1ai
DOMAINS_easysite102=easysite102.ru,www.easysite102.ru
DOMAINS_valitovgaziz=valitovgaziz.ru,www.valitovgaziz.ru
DOMAINS_yalarba=yalarba.ru,www.yalarba.ru
# keycloak
KEYCLOAK_ADMIN_PASSWORD=your_secure_password
KEYCLOAK_DB_PASSWORD=your_secure_db_password
+177
View File
@@ -0,0 +1,177 @@
# 🏃‍♂️ Бегущий Башкир - Беговой Клуб
![Docker](https://img.shields.io/badge/Docker-✓-blue)
![Vue.js](https://img.shields.io/badge/Vue.js-3.x-green)
![Go](https://img.shields.io/badge/Go-1.25+-00ADD8)
![PostgreSQL](https://img.shields.io/badge/PostgreSQL-15+-336791)
Полнофункциональная платформа для бегового клуба "Бегущий Башкир" с современной веб-архитектурой.
## 🎯 О проекте
**Бегущий Башкир** — это цифровая экосистема для бегового сообщества Башкортостана, объединяющая:
- 🌐 **Веб-сайт** (Vue.js SPA)
- 🔧 **REST API** (Go + PostgreSQL)
- 📊 **Аналитику** (Node.js)
- 🔐 **Автоматическую SSL** (Certbot)
- 🐳 **Контейнеризацию** (Docker Compose)
## 🏗️ Архитектура
```
┌─────────────────────────────────────────┐
│ Docker Compose Cluster │
├─────────────────────────────────────────┤
│ Nginx → API_BB (Go) → DB_BB (Postgres) │
│ ↑ ↑ ↑ │
│ Certbot Analytics Frontend │
│ (SSL) (Node.js) (Vue.js) │
└─────────────────────────────────────────┘
```
## ✨ Основные возможности
-**Управление тренировками** (дистанция, темп, калории)
-**Система событий** (забеги, тренировки, семинары)
-**Достижения и награды** с верификацией
-**Персональная статистика** с графиками
-**Галерея фотографий** мероприятий
-**Многоязычность** (русский/башкирский)
-**Адаптивный дизайн** (mobile-first)
-**Автоматические SSL сертификаты**
-**Health checks и мониторинг**
## 🚀 Быстрый старт
### Требования
- Docker 20.10+
- Docker Compose 2.0+
- 2 ГБ RAM
- Порты 80, 443 открыты
### Установка
```bash
# 1. Клонирование
git clone <repository>
cd project
# 2. Настройка окружения
cp .env.example .env
# Отредактируйте .env файл
# 3. Запуск
docker-compose up -d
# 4. Проверка
docker-compose ps
```
Сайт будет доступен по адресу: **https://ваш-домен**
## 📁 Структура проекта
```
├── BB/ # Бегущий Башкир
│ ├── api_bb/ # Go API сервер
│ └── bbvue/ # Vue.js фронтенд
├── nginx/ # Веб-сервер
├── certbot/ # SSL сертификаты
├── valitovgaziz/analytics/ # Аналитика
└── docker-compose.yml # Оркестрация
```
## 🔧 Технологический стек
**Backend:**
- Go 1.25+ (Chi, GORM, JWT)
- PostgreSQL 15
- Node.js (аналитика)
**Frontend:**
- Vue 3 (Composition API)
- Pinia (state management)
- Vue Router
- Vite
**Инфраструктура:**
- Docker & Docker Compose
- Nginx (обратный прокси)
- Certbot (Let's Encrypt)
## 📡 API Endpoints
```
GET /api/health # Проверка здоровья
POST /api/auth/register # Регистрация
POST /api/auth/login # Вход
GET /api/user/workouts # Тренировки пользователя
POST /api/events/register # Регистрация на событие
GET /api/achievements # Достижения
```
Полная документация API: [docs/api.md](docs/api.md)
## 🛠️ Разработка
```bash
# Запуск в режиме разработки
docker-compose -f docker-compose.dev.yml up
# Просмотр логов
docker-compose logs -f api_bb
# Остановка
docker-compose down
# Полная очистка
docker-compose down -v
```
## 📊 Мониторинг
- **Health checks:** `GET /api/health`
- **Статус сервисов:** `docker-compose ps`
- **Логи в реальном времени:** `docker-compose logs -f`
- **Использование ресурсов:** `docker stats`
## 🔒 Безопасность
- JWT аутентификация
- HTTPS (автоматические SSL)
- Хеширование паролей (bcrypt)
- Защита от SQL-инъекций
- CORS политики
- Валидация входных данных
## 🤝 Вклад в проект
1. Форкните репозиторий
2. Создайте ветку для фичи (`git checkout -b feature/amazing`)
3. Закоммитьте изменения (`git commit -m 'Add amazing feature'`)
4. Запушьте в ветку (`git push origin feature/amazing`)
5. Откройте Pull Request
## 📞 Контакты
- **Сайт:** [begushiybashkir.ru](https://begushiybashkir.ru)
- **Тренер:** +7 (927) 30-93-095
- **Telegram:** @begushiybashkir
- **Email:** zog1r@mail.ru
## 📄 Лицензия
Проект использует смешанные лицензии в зависимости от компонентов. Подробнее в файлах LICENSE соответствующих директорий.
## 🙏 Благодарности
- Команде разработчиков
- Участникам бегового клуба
- Сообществу Open Source
---
**🏆 Беги вместе с нами!**
**🏃‍♂️ Бегущий Башкир — больше чем бег, это образ жизни!**
---
*Обновлено: Декабрь 2025 | Версия: 1.0*
+24
View File
@@ -0,0 +1,24 @@
PORT=8080
DB_HOST=localhost
DB_PORT=5432
DB_USER=postgres
DB_PASSWORD=postgres
DB_NAME=bb_db
DB_SSLMODE=disable
# .env
LOG_LEVEL=debug
ENVIRONMENT=development
# app
REST_API_VERSION=1.0.0
VITE_API_BASE_URL=https://begushiybashkir.ru
# Email Configuration
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USERNAME=valitovgaziz
SMTP_PASSWORD=omqywxnamignyeql
FROM_EMAIL=valitovgaziz@gmail.com
FRONTEND_URL=https://begushiybashkir.ru
+17
View File
@@ -0,0 +1,17 @@
# Используем официальный образ Go
FROM golang:1.26.0-alpine
WORKDIR /app
# Копируем весь исходный код
COPY . .
# Скачиваем зависимости
RUN go mod tidy && go mod download
# Компилируем БЕЗ CGO
RUN CGO_ENABLED=0 GOOS=linux go build -o bin/main ./cmd/main.go
EXPOSE 8080
CMD ["./bin/main"]
Binary file not shown.
+64
View File
@@ -0,0 +1,64 @@
// main.go с graceful shutdown
package main
import (
"log"
"os"
"os/signal"
"syscall"
"api_bb/internal/app"
"api_bb/internal/config"
"api_bb/pkg/logger"
"go.uber.org/zap"
)
func main() {
// Загрузка конфигурации
cfg := config.Load()
// Инициализация логгера
if err := logger.Init(
os.Getenv("LOG_LEVEL"),
os.Getenv("ENVIRONMENT"),
); err != nil {
log.Printf("Failed to initialize logger: %v", err)
os.Exit(1)
}
defer logger.Sync()
// Логируем начало работы
logger.LogApplicationStart(os.Getenv("REST_API_VERSION"), os.Getenv("ENVIRONMENT"), "")
// Создание и инициализация приложения
application := app.NewApp(cfg)
if err := application.Initialize(); err != nil {
logger.Get().Fatal("failed to initialize application", zap.Error(err))
}
// Канал для graceful shutdown
done := make(chan bool, 1)
quit := make(chan os.Signal, 1)
signal.Notify(quit, os.Interrupt, syscall.SIGTERM)
// Запуск сервера в горутине
go func() {
if err := application.Start(); err != nil {
logger.Get().Fatal("failed to start server", zap.Error(err))
}
done <- true
}()
// Ожидание сигнала shutdown
<-quit
logger.Get().Info("shutdown signal received")
// Graceful shutdown приложения
if err := application.Shutdown(); err != nil {
logger.Get().Fatal("could not gracefully shutdown the application", zap.Error(err))
}
logger.LogApplicationShutdown("graceful shutdown")
<-done
}
+372
View File
@@ -0,0 +1,372 @@
# Документация проекта API бегущего башкира
## Обзор проекта
**API бегущего башкира** — это REST API сервис для бегового сообщества Башкортостана, разработанный на Go с использованием современных технологий и архитектурных подходов. Проект предоставляет комплексную платформу для управления тренировками, событиями, достижениями и взаимодействия между бегунами.
**Текущая версия**: v1 (декабрь 2025)
## Стек технологий
### Основные технологии
- **Язык**: Go 1.25.1
- **Веб-фреймворк**: Chi v5 (роутер)
- **ORM**: GORM v1.31.0 с драйвером PostgreSQL
- **Аутентификация**: JWT (golang-jwt/jwt/v5)
- **Валидация**: validator/v10
### Вспомогательные библиотеки
- **Логирование**: Zap
- **Конфигурация**: godotenv
- **Работа с email**: go-mail
- **Хеширование паролей**: bcrypt (golang.org/x/crypto)
- **CORS**: go-chi/cors
- **UUID**: google/uuid
### База данных
- **Основная СУБД**: PostgreSQL
- **Миграции**: встроенные через GORM AutoMigrate
## Архитектура проекта
### Структура каталогов
```
api_bb/
├── cmd/
│ └── server/
│ └── main.go # Точка входа
├── internal/
│ ├── config/
│ │ └── config.go # Конфигурация приложения
│ ├── handlers/
│ │ ├── handlers.go # Основной обработчик
│ │ ├── health.go # Health check endpoints
│ │ ├── auth.go # Аутентификация
│ │ └── ... другие обработчики
│ ├── models/
│ │ ├── user.go # Модель пользователя
│ │ ├── workout.go # Модель тренировки
│ │ ├── event.go # Модель события
│ │ ├── achievement.go # Модель достижений
│ │ ├── training_plan.go # Модель плана тренировок
│ │ ├── news.go # Модель новостей
│ │ ├── review.go # Модель отзывов
│ │ ├── gallery.go # Модель галереи
│ │ ├── personal_best.go # Модель личных рекордов
│ │ ├── user_stats.go # Модель статистики пользователя
│ │ ├── email.go # Модель email верификации
│ │ └── common.go # Общие DTO и структуры
│ ├── repository/
│ │ └── user_repository.go # Репозиторий пользователей
│ │ └── ... другие репозитории
│ ├── service/
│ │ └── auth_service.go # Сервис аутентификации
│ │ └── ... другие сервисы
│ └── routes/
│ └── routes.go # Настройка маршрутов
├── pkg/
│ ├── database/
│ │ └── database.go # Подключение к БД
│ └── middleware/
│ └── middleware.go # Middleware функции
├── go.mod # Зависимости
└── go.sum # Точные версии зависимостей
```
### Архитектурные принципы
1. **Чистая архитектура**: Разделение на слои (handler → service → repository → model)
2. **Domain-Driven Design**: Бизнес-модели в центре архитектуры
3. **Dependency Injection**: Внедрение зависимостей через конструкторы
4. **REST API**: Стандартные RESTful endpoints
5. **JWT аутентификация**: Stateless авторизация
## Основные сущности
### Пользователь (User)
- **Основные поля**: ID, email, имя, фамилия, аватар, телефон
- **Дополнительно**: опыт, цели, подписка на рассылку
- **Роли**: user, admin
- **Аутентификация**: email + password, JWT токены
### Тренировка (Workout)
- **Типы**: easy, tempo, interval, long, recovery
- **Метрики**: дистанция, продолжительность, темп, калории
- **Дополнительно**: заметки, дата тренировки
### Событие (Event)
- **Типы**: race (забег), training (тренировка), social (встреча), workshop (семинар)
- **Характеристики**: дата, место, дистанция, количество участников
- **Регистрация**: открытая/закрытая, с ограничениями
### Достижения (Achievement)
- **Типы**: distance (дистанция), speed (скорость), consistency (последовательность), event (событие), special (особые)
- **Подтверждение**: верифицированные/неверифицированные
- **Бейджи**: изображения достижений
### Личные рекорды (Personal Best)
- **Дистанции**: 5к, 10к, полумарафон, марафон, другие
- **Данные**: время, темп, дата, место проведения
- **Подтверждение**: верифицированные результаты
### Новости (News)
- **Категории**: events, training, achievements, community
- **Контент**: заголовок, краткое описание, полный текст, изображение
- **Взаимодействие**: просмотры, комментарии
### Отзывы (Review)
- **Оценка**: 1-5 звезд
- **Дополнительно**: достижения, дистанции, улучшения
- **Верификация**: подтвержденные отзывы
### Галерея (Gallery)
- **Категории**: training, events, community, achievements
- **Контент**: фотографии с описанием
- **Взаимодействие**: просмотры, лайки
### Тренировочные планы (Training Plan)
- **Структура**: недельные планы с тренировками
- **Прогресс**: текущая неделя, завершенные тренировки
- **Цели**: целевая дата и дистанция
## Маршруты API
### Группы маршрутов
#### 1. Аутентификация и пользователи
```
POST /v1/auth/register # Регистрация
POST /v1/auth/login # Вход
POST /v1/auth/logout # Выход
GET /v1/user/profile # Профиль пользователя
POST /v1/user/editProfile # Обновление профиля
```
#### 2. Тренировки
```
POST /v1/user/workouts # Создание тренировки
GET /v1/user/workouts # Получение тренировок
GET /v1/user/workouts/stats # Статистика тренировок
PUT /v1/user/workouts/{id} # Обновление тренировки
DELETE /v1/user/workouts/{id} # Удаление тренировки
```
#### 3. События
```
GET /v1/events # Все события
GET /v1/events/upcoming # Предстоящие события
POST /v1/events/register # Регистрация на событие
GET /v1/events/my/registrations # Мои регистрации
```
#### 4. Достижения
```
POST /v1/user/achievements # Создание достижения
GET /v1/user/achievements # Мои достижения
GET /v1/achievements/user/{id} # Достижения пользователя (публичные)
PUT /v1/user/achievements/{id} # Обновление достижения
```
#### 5. Личные рекорды
```
POST /v1/user/personal-bests # Создание рекорда
GET /v1/user/personal-bests # Мои рекорды
GET /v1/user/personal-bests/summary # Сводка рекордов
PUT /v1/user/personal-bests/{id} # Обновление рекорда
```
#### 6. Новости
```
GET /v1/news # Все новости
GET /v1/news/{id} # Конкретная новость
POST /v1/news # Создание новости (админ)
POST /v1/news/{id}/comments # Комментарий к новости
```
#### 7. Отзывы
```
GET /v1/reviews # Все отзывы
GET /v1/reviews/stats # Статистика отзывов
POST /v1/reviews # Создание отзыва
GET /v1/reviews/my # Мои отзывы
```
#### 8. Верификация email и сброс пароля
```
GET /v1/verify-email # Верификация email
POST /v1/auth/verify-email/resend # Повторная отправка
POST /v1/auth/password-reset/request # Запрос сброса
POST /v1/auth/password-reset/confirm # Подтверждение сброса
```
## Аутентификация и авторизация
### JWT Токены
- **Access Token**: краткосрочный (15-60 минут)
- **Refresh Token**: долгосрочный (7-30 дней)
- **Хранение**: HTTP-only cookies или заголовок Authorization
### Middleware
1. **AuthMiddleware**: проверка JWT токена
2. **RequireAuth**: требование аутентификации
3. **AdminMiddleware**: проверка прав администратора
## Безопасность
### Меры защиты
1. **Хеширование паролей**: bcrypt
2. **SQL инъекции**: защита через GORM
3. **CORS**: настройка политик
4. **Валидация**: входных данных
5. **Лимиты запросов**: предотвращение DDoS
### Безопасность данных
- Чувствительные данные не возвращаются в ответах
- Пароли никогда не логируются
- Сессии через JWT (stateless)
## Конфигурация
### Переменные окружения
```
DB_HOST=localhost
DB_PORT=5432
DB_USER=postgres
DB_PASSWORD=secret
DB_NAME=api_bb
JWT_SECRET=your-secret-key
EMAIL_HOST=smtp.gmail.com
EMAIL_PORT=587
EMAIL_USER=your-email@gmail.com
EMAIL_PASSWORD=your-password
```
### Конфигурационный файл
```go
type Config struct {
DBHost string
DBPort string
DBUser string
DBPassword string
DBName string
JWTSecret string
Email EmailConfig
}
```
## Развертывание
### Требования
1. Go 1.25+
2. PostgreSQL 12+
3. SMTP сервер для email
### Шаги развертывания
```bash
# Клонирование репозитория
git clone <repository-url>
cd api_bb
# Установка зависимостей
go mod download
# Настройка переменных окружения
cp .env.example .env
# редактирование .env
# Запуск миграций
go run cmd/server/main.go migrate
# Запуск сервера
go run cmd/server/main.go serve
# Или сборка
go build -o api_bb cmd/server/main.go
./api_bb serve
```
## Тестирование
### Типы тестов
1. **Юнит-тесты**: тестирование отдельных функций
2. **Интеграционные тесты**: тестирование взаимодействия с БД
3. **E2E тесты**: тестирование API endpoints
### Запуск тестов
```bash
# Все тесты
go test ./...
# С покрытием
go test -cover ./...
# Конкретный пакет
go test ./internal/handlers
```
## Мониторинг и логирование
### Логирование
- **Уровни**: Debug, Info, Warn, Error
- **Форматы**: JSON (продакшн), Text (разработка)
- **Контекст**: request ID, пользователь, время выполнения
### Health checks
```
GET /api/health # Общая проверка
GET /api/check # Детальная проверка
```
### Метрики
- Количество запросов
- Время ответа
- Ошибки по типам
- Использование ресурсов
## Масштабирование
### Горизонтальное масштабирование
1. **Stateless архитектура**: легкое добавление инстансов
2. **Балансировка нагрузки**: через nginx или cloud load balancer
3. **Кэширование**: Redis для частых запросов
### Вертикальное масштабирование
1. **Оптимизация запросов**: индексы в БД
2. **Connection pooling**: настройка пулов соединений
3. **Асинхронная обработка**: фоновые задачи
## Дорожная карта развития
### Ближайшие планы
1. **WebSocket**: реальное время для событий
2. **Push-уведомления**: через Firebase Cloud Messaging
3. **Интеграция с Strava**: импорт тренировок
4. **Социальные функции**: друзья, группы, чаты
### Долгосрочные цели
1. **Мобильное приложение**: React Native
2. **Аналитика**: расширенная статистика
3. **Партнерская программа**: интеграция с магазинами
4. **Интернационализация**: поддержка других языков
## Контакты и поддержка
### Команда проекта
- **Разработка**: [Ваши контакты]
- **Дизайн**: [Ваши контакты]
- **Контент**: [Ваши контакты]
### Документация
- **API документация**: Swagger/OpenAPI
- **Руководство пользователя**: на сайте begushiybashkir.ru
- **Разработчикам**: README и примеры кода
### Сообщество
- **Telegram канал**: [ссылка]
- **Группа VK**: [ссылка]
- **Instagram**: [ссылка]
---
*Документация обновлена: декабрь 2025 года*
*Проект находится в активной разработке. API может изменяться.*
+43
View File
@@ -0,0 +1,43 @@
module api_bb
go 1.25.1
require (
github.com/go-chi/chi/v5 v5.2.3
github.com/go-chi/cors v1.2.2
github.com/golang-jwt/jwt/v5 v5.3.0
github.com/golang-migrate/migrate/v4 v4.18.2
golang.org/x/crypto v0.43.0
gorm.io/driver/postgres v1.6.0
gorm.io/gorm v1.31.0
)
require (
github.com/gabriel-vasile/mimetype v1.4.10 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/lib/pq v1.10.9 // indirect
github.com/stretchr/testify v1.11.1 // indirect
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/multierr v1.10.0 // indirect
golang.org/x/sys v0.37.0 // indirect
)
require (
github.com/go-playground/validator/v10 v10.28.0
github.com/google/uuid v1.6.0
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/pgx/v5 v5.6.0 // indirect
github.com/jackc/puddle/v2 v2.2.2 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/joho/godotenv v1.5.1
github.com/wneessen/go-mail v0.7.2
go.uber.org/zap v1.27.0
golang.org/x/sync v0.17.0 // indirect
golang.org/x/text v0.30.0 // indirect
)
+68
View File
@@ -0,0 +1,68 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gabriel-vasile/mimetype v1.4.10 h1:zyueNbySn/z8mJZHLt6IPw0KoZsiQNszIpU+bX4+ZK0=
github.com/gabriel-vasile/mimetype v1.4.10/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
github.com/go-chi/chi/v5 v5.2.3 h1:WQIt9uxdsAbgIYgid+BpYc+liqQZGMHRaUwp0JUcvdE=
github.com/go-chi/chi/v5 v5.2.3/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
github.com/go-chi/cors v1.2.2 h1:Jmey33TE+b+rB7fT8MUy1u0I4L+NARQlK6LhzKPSyQE=
github.com/go-chi/cors v1.2.2/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.28.0 h1:Q7ibns33JjyW48gHkuFT91qX48KG0ktULL6FgHdG688=
github.com/go-playground/validator/v10 v10.28.0/go.mod h1:GoI6I1SjPBh9p7ykNE/yj3fFYbyDOpwMn5KXd+m2hUU=
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY=
github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw=
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/wneessen/go-mail v0.7.2 h1:xxPnhZ6IZLSgxShebmZ6DPKh1b6OJcoHfzy7UjOkzS8=
github.com/wneessen/go-mail v0.7.2/go.mod h1:+TkW6QP3EVkgTEqHtVmnAE/1MRhmzb8Y9/W3pweuS+k=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4=
gorm.io/driver/postgres v1.6.0/go.mod h1:vUw0mrGgrTK+uPHEhAdV4sfFELrByKVGnaVRkXDhtWo=
gorm.io/gorm v1.31.0 h1:0VlycGreVhK7RF/Bwt51Fk8v0xLiiiFdbGDPIZQ7mJY=
gorm.io/gorm v1.31.0/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs=
+104
View File
@@ -0,0 +1,104 @@
package app
import (
"context"
"net/http"
"time"
"go.uber.org/zap"
"gorm.io/gorm"
"api_bb/internal/config"
"api_bb/internal/database"
"api_bb/internal/routes"
"api_bb/pkg/logger"
)
type App struct {
cfg *config.Config
db *database.Database
server *http.Server
}
func NewApp(cfg *config.Config) *App {
return &App{
cfg: cfg,
}
}
// Initialize инициализирует приложение (БД, миграции, роутинг)
func (a *App) Initialize() error {
zapLogger := logger.Get()
// Инициализация базы данных
dbConfig := &database.Config{
URL: a.cfg.DatabaseURL,
Schema: a.cfg.DBSchema,
}
a.db = database.NewDatabase(dbConfig)
// Подключение к БД
if err := a.db.Connect(); err != nil {
return err
}
// Проверка соединения
if err := a.db.Ping(); err != nil {
return err
}
// Настройка роутера
router := routes.SetupRouter(a.db.DB, a.cfg)
// Настройка HTTP сервера
a.server = &http.Server{
Addr: ":" + a.cfg.Port,
Handler: router,
}
zapLogger.Info("application initialized successfully")
return nil
}
// Start запускает HTTP сервер
func (a *App) Start() error {
zapLogger := logger.Get()
zapLogger.Info("starting HTTP server", zap.String("port", a.cfg.Port))
if err := a.server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
return err
}
return nil
}
// Shutdown gracefully останавливает приложение
func (a *App) Shutdown() error {
zapLogger := logger.Get()
zapLogger.Info("shutdown signal received")
// Graceful shutdown сервера
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
a.server.SetKeepAlivesEnabled(false)
if err := a.server.Shutdown(ctx); err != nil {
zapLogger.Error("could not gracefully shutdown the server", zap.Error(err))
return err
}
// Закрытие соединения с БД
if err := a.db.Close(); err != nil {
return err
}
zapLogger.Info("application shutdown completed")
return nil
}
// GetDB возвращает экземпляр базы данных
func (a *App) GetDB() *gorm.DB {
return a.db.DB
}
@@ -0,0 +1,61 @@
// config/config.go
package config
import (
"fmt"
"os"
"github.com/joho/godotenv"
)
type Config struct {
Port string
DatabaseURL string
DBSchema string
StaticURL string `env:"STATIC_URL" envDefault:"http://localhost:8080"`
JWTSecret string `env:"JWT_SECRET,required"`
// Email configuration
SMTPHost string `env:"SMTP_HOST,required"`
SMTPPort int `env:"SMTP_PORT,required"`
SMTPUsername string `env:"SMTP_USERNAME,required"`
SMTPPassword string `env:"SMTP_PASSWORD,required"`
FromEmail string `env:"FROM_EMAIL,required"`
FrontendURL string `env:"FRONTEND_URL,required"`
}
func Load() *Config {
_ = godotenv.Load(".env")
port := getEnv("PORT", "8080")
jwtSecret := getEnv("JWT_SECRET", "your-secret-key")
// Формируем DSN для PostgreSQL из переменных окружения
databaseURL := getPostgresDSN()
return &Config{
Port: port,
DatabaseURL: databaseURL,
DBSchema: getEnv("DB_SCHEMA", "public"),
JWTSecret: jwtSecret,
}
}
func getPostgresDSN() string {
host := getEnv("DB_HOST", "localhost")
port := getEnv("DB_PORT", "5432")
user := getEnv("DB_USER", "postgres")
password := getEnv("DB_PASSWORD", "postgres")
dbname := getEnv("DB_NAME", "bb_db")
sslmode := getEnv("DB_SSLMODE", "disable")
return fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=%s",
host, port, user, password, dbname, sslmode)
}
func getEnv(key, defaultValue string) string {
value := os.Getenv(key)
if value == "" {
return defaultValue
}
return value
}
@@ -0,0 +1,183 @@
package database
import (
"api_bb/migrations"
"database/sql"
"fmt"
"strings"
"time"
"github.com/golang-migrate/migrate/v4"
migratepg "github.com/golang-migrate/migrate/v4/database/postgres"
"github.com/golang-migrate/migrate/v4/source/iofs"
"go.uber.org/zap"
gormpg "gorm.io/driver/postgres"
"gorm.io/gorm"
"api_bb/pkg/logger"
)
type Database struct {
DB *gorm.DB
cfg *Config
}
type Config struct {
URL string
Schema string
}
func NewDatabase(cfg *Config) *Database {
if cfg.Schema == "" {
cfg.Schema = "public"
}
return &Database{
cfg: cfg,
}
}
func (d *Database) Connect() error {
zapLogger := logger.Get()
zapLogger.Info("attempting to connect to database",
zap.String("host", ExtractHostFromDSN(d.cfg.URL)),
zap.String("database", ExtractDBNameFromDSN(d.cfg.URL)),
zap.String("schema", d.cfg.Schema),
)
dsn := d.cfg.URL
if d.cfg.Schema != "public" {
dsn = dsn + fmt.Sprintf(" search_path=%s", d.cfg.Schema)
}
db, err := gorm.Open(gormpg.Open(dsn), &gorm.Config{})
if err != nil {
zapLogger.Error("failed to connect to database",
zap.Error(err),
zap.String("database_url", MaskPassword(d.cfg.URL)),
)
return fmt.Errorf("failed to connect to database: %w", err)
}
d.DB = db
zapLogger.Info("Configure connection pool")
sqlDB, err := d.DB.DB()
if err != nil {
return fmt.Errorf("failed to get underlying sql.DB: %w", err)
}
sqlDB.SetMaxOpenConns(25)
sqlDB.SetMaxIdleConns(10)
sqlDB.SetConnMaxLifetime(30 * time.Minute)
zapLogger.Info("Run database migrations")
if err := d.runMigrations(sqlDB); err != nil {
return fmt.Errorf("failed to run migrations: %w", err)
}
zapLogger.Info("Migrations completed successfully")
zapLogger.Info("successfully connected to database",
zap.String("host", ExtractHostFromDSN(d.cfg.URL)),
zap.String("database", ExtractDBNameFromDSN(d.cfg.URL)),
)
return nil
}
func (d *Database) runMigrations(sqlDB *sql.DB) error {
zapLogger := logger.Get()
source, err := iofs.New(migrations.FS, ".")
if err != nil {
return fmt.Errorf("failed to create migration source: %w", err)
}
driver, err := migratepg.WithInstance(sqlDB, &migratepg.Config{})
if err != nil {
return fmt.Errorf("failed to create postgres driver: %w", err)
}
m, err := migrate.NewWithInstance("iofs", source, "postgres", driver)
if err != nil {
return fmt.Errorf("failed to create migrate instance: %w", err)
}
if err := m.Up(); err != nil && err != migrate.ErrNoChange {
zapLogger.Error("Migration error", zap.Error(err))
return fmt.Errorf("failed to apply migrations: %w", err)
}
return nil
}
func (d *Database) Ping() error {
zapLogger := logger.Get()
sqlDB, err := d.DB.DB()
if err != nil {
zapLogger.Error("failed to get database instance", zap.Error(err))
return fmt.Errorf("failed to get database instance: %w", err)
}
if err := sqlDB.Ping(); err != nil {
zapLogger.Error("database ping failed", zap.Error(err))
return fmt.Errorf("database ping failed: %w", err)
}
zapLogger.Info("database ping successful")
return nil
}
func (d *Database) Close() error {
zapLogger := logger.Get()
if d.DB == nil {
return nil
}
sqlDB, err := d.DB.DB()
if err != nil {
zapLogger.Error("failed to get database instance for closing", zap.Error(err))
return fmt.Errorf("failed to get database instance: %w", err)
}
zapLogger.Info("closing database connection")
if err := sqlDB.Close(); err != nil {
zapLogger.Error("failed to close database connection", zap.Error(err))
return fmt.Errorf("failed to close database connection: %w", err)
}
zapLogger.Info("database connection closed successfully")
return nil
}
func ExtractHostFromDSN(dsn string) string {
parts := strings.Split(dsn, " ")
for _, part := range parts {
if strings.HasPrefix(part, "host=") {
return strings.TrimPrefix(part, "host=")
}
}
return "unknown"
}
func ExtractDBNameFromDSN(dsn string) string {
parts := strings.Split(dsn, " ")
for _, part := range parts {
if strings.HasPrefix(part, "dbname=") {
return strings.TrimPrefix(part, "dbname=")
}
}
return "unknown"
}
func MaskPassword(dsn string) string {
parts := strings.Split(dsn, " ")
for i, part := range parts {
if strings.HasPrefix(part, "password=") {
parts[i] = "password=***"
break
}
}
return strings.Join(parts, " ")
}
+266
View File
@@ -0,0 +1,266 @@
// handlers/auth.go
package handlers
import (
"bytes"
"encoding/json"
"io"
"net/http"
"strings"
"time"
"api_bb/internal/models"
"api_bb/internal/service"
"api_bb/pkg/logger"
"api_bb/pkg/utils"
"go.uber.org/zap"
)
type AuthHandler struct {
authService service.AuthService
jwtService service.JWTService
logger logger.LoggerInterface
emailService service.EmailService
}
func NewAuthHandler(authService service.AuthService, jwtService service.JWTService, emailService service.EmailService) *AuthHandler {
return &AuthHandler{
authService: authService,
jwtService: jwtService,
logger: logger.NewWrapper(logger.Get().With(zap.String("handler", "auth"))),
emailService: emailService,
}
}
type RegisterRequest struct {
Email string `json:"email"`
Password string `json:"password"`
FirstName string `json:"firstName"`
LastName string `json:"lastName"`
Phone string `json:"phone"`
Experience string `json:"experience"`
Goals string `json:"goals"`
Newsletter bool `json:"newsletter"`
}
type LoginRequest struct {
Email string `json:"email"`
Password string `json:"password"`
}
func (h *AuthHandler) Register(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling register request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Логируем тело запроса для отладки
bodyBytes, err := io.ReadAll(r.Body)
if err != nil {
h.logger.Error("failed to read request body", zap.Error(err))
utils.RespondWithError(w, http.StatusBadRequest, "Failed to read request body: "+err.Error())
return
}
// Восстанавливаем тело для дальнейшего использования
r.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
h.logger.Debug("raw register request body", zap.String("body", string(bodyBytes)))
var req RegisterRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
h.logger.Error("failed to decode JSON payload", zap.Error(err))
utils.RespondWithError(w, http.StatusBadRequest, "Invalid JSON payload: "+err.Error())
return
}
h.logger.Info("parsed register request",
zap.String("email", req.Email),
zap.String("first_name", req.FirstName),
zap.String("last_name", req.LastName),
)
// Валидация обязательных полей
if req.FirstName == "" {
h.logger.Warn("register failed - first name required")
utils.RespondWithError(w, http.StatusBadRequest, "First name is required")
return
}
if req.LastName == "" {
h.logger.Warn("register failed - last name required")
utils.RespondWithError(w, http.StatusBadRequest, "Last name is required")
return
}
if req.Email == "" {
h.logger.Warn("register failed - email required")
utils.RespondWithError(w, http.StatusBadRequest, "Email is required")
return
}
if req.Password == "" {
h.logger.Warn("register failed - password required")
utils.RespondWithError(w, http.StatusBadRequest, "Password is required")
return
}
if len(req.Password) < 6 {
h.logger.Warn("register failed - password too short")
utils.RespondWithError(w, http.StatusBadRequest, "Password must be at least 6 characters")
return
}
user := &models.User{
Email: req.Email,
Password: req.Password,
FirstName: req.FirstName,
LastName: req.LastName,
Phone: req.Phone,
Experience: req.Experience,
Goals: req.Goals,
Newsletter: req.Newsletter,
Role: "user",
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
if err := h.authService.Register(user); err != nil {
h.logger.Error("auth service registration failed",
zap.String("email", req.Email),
zap.Error(err),
)
utils.RespondWithError(w, http.StatusBadRequest, err.Error())
return
}
h.logger.Info("user registered successfully",
zap.Uint("user_id", user.ID),
zap.String("email", user.Email),
)
// Отправки сообщения для верификации Email
if err := h.emailService.SendVerificationEmail(user.ID, user.Email, user.FirstName); err != nil {
h.logger.Error("failed to send verification email",
zap.Error(err),
zap.Uint("user_id", user.ID))
}
// После успешной регистрации возвращаем данные пользователя
utils.RespondWithJSON(w, http.StatusCreated, map[string]interface{}{
"message": "User registered successfully",
"user": toUserResponse(user),
})
}
func (h *AuthHandler) Login(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling login request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Проверяем Content-Type
if r.Header.Get("Content-Type") != "application/json" {
h.logger.Warn("invalid content type", zap.String("content_type", r.Header.Get("Content-Type")))
utils.RespondWithError(w, http.StatusBadRequest, "Content-Type must be application/json")
return
}
// Читаем и логируем тело запроса
bodyBytes, err := io.ReadAll(r.Body)
if err != nil {
h.logger.Error("failed to read request body", zap.Error(err))
utils.RespondWithError(w, http.StatusBadRequest, "Failed to read request body")
return
}
defer r.Body.Close()
// Восстанавливаем тело
r.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
h.logger.Debug("request body", zap.String("body", string(bodyBytes)))
var req LoginRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
h.logger.Error("JSON decode failed",
zap.Error(err),
zap.String("raw_body", string(bodyBytes)),
)
utils.RespondWithError(w, http.StatusBadRequest, "Invalid JSON: "+err.Error())
return
}
req.Email = strings.TrimSpace(req.Email)
req.Password = strings.TrimSpace(req.Password)
// Валидация
if req.Email == "" || req.Password == "" {
h.logger.Warn("validation failed",
zap.String("email", req.Email),
zap.Int("password_len", len(req.Password)),
)
utils.RespondWithError(w, http.StatusBadRequest, "Email and password are required")
return
}
h.logger.Info("attempting login", zap.String("email", req.Email))
user, token, err := h.authService.Login(req.Email, req.Password)
if err != nil {
h.logger.Warn("login failed", zap.String("email", req.Email), zap.Error(err))
utils.RespondWithError(w, http.StatusUnauthorized, err.Error())
return
}
// Устанавливаем куки
http.SetCookie(w, &http.Cookie{
Name: "auth_token",
Value: token,
Path: "/",
HttpOnly: true,
Secure: false,
SameSite: http.SameSiteLaxMode,
Expires: time.Now().Add(24 * time.Hour),
})
h.logger.Info("login successful",
zap.Uint("user_id", user.ID),
zap.String("email", user.Email),
)
utils.RespondWithJSON(w, http.StatusOK, map[string]interface{}{
"message": "Login successful",
"token": token,
"user": toUserResponse(user),
})
}
func (h *AuthHandler) Logout(w http.ResponseWriter, r *http.Request) {
// Устанавливаем CORS заголовки
w.Header().Set("Access-Control-Allow-Origin", r.Header.Get("Origin"))
w.Header().Set("Access-Control-Allow-Credentials", "true")
h.logger.Info("handling logout request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Удаляем куку
http.SetCookie(w, &http.Cookie{
Name: "auth_token",
Value: "",
Path: "/",
HttpOnly: true,
Secure: false,
SameSite: http.SameSiteLaxMode,
Expires: time.Now().Add(-1 * time.Hour),
MaxAge: -1,
})
h.logger.Info("user logged out successfully")
utils.RespondWithJSON(w, http.StatusOK, map[string]string{
"message": "Logout successful",
})
}
@@ -0,0 +1,239 @@
// handlers/avatar.go
package handlers
import (
"net/http"
"strings"
"time"
"api_bb/internal/service"
"api_bb/pkg/logger"
"api_bb/pkg/middleware"
"api_bb/pkg/utils"
"github.com/go-chi/chi/v5"
"go.uber.org/zap"
)
type AvatarHandler struct {
logger logger.LoggerInterface
avatarService service.AvatarService
}
func NewAvatarHandler(avatarService service.AvatarService) *AvatarHandler {
return &AvatarHandler{
logger: logger.NewWrapper(logger.Get().With(zap.String("handler", "avatar"))),
avatarService: avatarService,
}
}
func (h *AvatarHandler) UploadAvatar(w http.ResponseWriter, r *http.Request) {
h.logger.Debug("UploadAvatar START",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
defer func() {
h.logger.Debug("UploadAvatar END",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
)
}()
user, ok := middleware.GetUserFromContext(r.Context())
if !ok {
h.logger.Warn("UploadAvatar: authentication required")
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
h.logger.Debug("UploadAvatar: user authenticated",
zap.Int64("user_id", int64(user.ID)),
zap.String("username", user.FirstName+user.LastName),
)
// Парсим multipart форму
h.logger.Debug("UploadAvatar: parsing multipart form")
if err := r.ParseMultipartForm(10 << 20); err != nil { // 10MB limit
h.logger.Error("UploadAvatar: failed to parse form", zap.Error(err))
utils.RespondWithError(w, http.StatusBadRequest, "Failed to parse form: "+err.Error())
return
}
h.logger.Debug("UploadAvatar: getting file from form")
file, header, err := r.FormFile("avatar")
if err != nil {
h.logger.Error("UploadAvatar: failed to get file from form", zap.Error(err))
utils.RespondWithError(w, http.StatusBadRequest, "Failed to get file: "+err.Error())
return
}
defer file.Close()
h.logger.Debug("UploadAvatar: file received",
zap.String("filename", header.Filename),
zap.Int64("size", header.Size),
zap.String("content_type", header.Header.Get("Content-Type")),
)
// Проверяем тип файла
allowedTypes := map[string]bool{
"image/jpeg": true,
"image/jpg": true,
"image/png": true,
"image/gif": true,
}
contentType := header.Header.Get("Content-Type")
if !allowedTypes[contentType] {
h.logger.Warn("UploadAvatar: invalid file type",
zap.String("content_type", contentType),
zap.String("filename", header.Filename),
)
utils.RespondWithError(w, http.StatusBadRequest, "Only JPEG, PNG and GIF images are allowed")
return
}
h.logger.Debug("UploadAvatar: file type validated successfully")
// Загружаем аватар
h.logger.Debug("UploadAvatar: calling avatarService.UploadAvatar",
zap.Int64("user_id", int64(user.ID)),
)
avatarPath, err := h.avatarService.UploadAvatar(user.ID, file, header)
if err != nil {
h.logger.Error("UploadAvatar: failed to upload avatar", zap.Error(err))
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to upload avatar: "+err.Error())
return
}
h.logger.Info("UploadAvatar: avatar uploaded successfully",
zap.Int64("user_id", int64(user.ID)),
zap.String("avatar_path", avatarPath),
)
// Возвращаем ответ с полем success
utils.RespondWithJSON(w, http.StatusOK, map[string]interface{}{
"success": true,
"message": "Avatar uploaded successfully",
"avatar": avatarPath,
})
}
func (h *AvatarHandler) DeleteAvatar(w http.ResponseWriter, r *http.Request) {
h.logger.Debug("DeleteAvatar START",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
defer func() {
h.logger.Debug("DeleteAvatar END",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
)
}()
user, ok := middleware.GetUserFromContext(r.Context())
if !ok {
h.logger.Warn("DeleteAvatar: authentication required")
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
h.logger.Debug("DeleteAvatar: user authenticated",
zap.Int64("user_id", int64(user.ID)),
zap.String("username", user.FirstName+user.LastName),
)
h.logger.Debug("DeleteAvatar: calling avatarService.DeleteAvatar",
zap.Int64("user_id", int64(user.ID)),
)
if err := h.avatarService.DeleteAvatar(user.ID); err != nil {
h.logger.Error("DeleteAvatar: failed to delete avatar", zap.Error(err))
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to delete avatar: "+err.Error())
return
}
h.logger.Info("DeleteAvatar: avatar deleted successfully",
zap.Int64("user_id", int64(user.ID)),
)
// Возвращаем ответ с полем success
utils.RespondWithJSON(w, http.StatusOK, map[string]interface{}{
"success": true,
"message": "Avatar deleted successfully",
})
}
// GET /v1/user/avatars/{filename}
func (h *AvatarHandler) GetAvatar(w http.ResponseWriter, r *http.Request) {
filename := chi.URLParam(r, "filename")
h.logger.Debug("GetAvatar START",
zap.String("method", r.Method),
zap.String("filename", filename),
zap.String("remote_addr", r.RemoteAddr),
zap.String("url", r.URL.String()),
)
defer func() {
h.logger.Debug("GetAvatar END",
zap.String("method", r.Method),
zap.String("filename", filename),
)
}()
// Валидация имени файла
if filename == "" || strings.Contains(filename, "..") || strings.Contains(filename, "/") {
h.logger.Warn("GetAvatar: invalid filename", zap.String("filename", filename))
utils.RespondWithError(w, http.StatusBadRequest, "Invalid filename")
return
}
h.logger.Info("GetAvatar: handling get avatar request",
zap.String("method", r.Method),
zap.String("filename", filename),
zap.String("remote_addr", r.RemoteAddr),
)
// Используем ServeAvatarFile для обслуживания файла
h.logger.Debug("GetAvatar: calling avatarService.ServeAvatarFile",
zap.String("filename", filename),
)
contentType, err := h.avatarService.ServeAvatarFile(w, filename)
if err != nil {
h.logger.Warn("GetAvatar: failed to serve avatar file",
zap.String("filename", filename),
zap.Error(err),
)
switch {
case err.Error() == "avatar file not found":
h.logger.Warn("GetAvatar: avatar file not found", zap.String("filename", filename))
utils.RespondWithError(w, http.StatusNotFound, "Avatar not found")
case err.Error() == "invalid filename" || err.Error() == "unsupported file format":
h.logger.Warn("GetAvatar: invalid filename or format",
zap.String("filename", filename),
zap.Error(err),
)
utils.RespondWithError(w, http.StatusBadRequest, err.Error())
default:
h.logger.Error("GetAvatar: internal server error", zap.Error(err))
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to serve avatar")
}
return
}
// Устанавливаем заголовки для кэширования
h.logger.Debug("GetAvatar: setting response headers",
zap.String("content_type", contentType),
)
w.Header().Set("Content-Type", contentType)
w.Header().Set("Cache-Control", "public, max-age=31536000") // Кэш на 1 год
w.Header().Set("Expires", time.Now().Add(365*24*time.Hour).Format(http.TimeFormat))
h.logger.Info("GetAvatar: avatar served successfully",
zap.String("filename", filename),
zap.String("content_type", contentType),
)
}
@@ -0,0 +1,223 @@
// handlers/email_handler.go
package handlers
import (
"net/http"
"api_bb/internal/models"
"api_bb/internal/service"
"api_bb/pkg/logger"
"api_bb/pkg/middleware"
"api_bb/pkg/utils"
"go.uber.org/zap"
)
type EmailHandler struct {
logger logger.LoggerInterface
emailService *service.EmailService
}
func NewEmailHandler(emailService *service.EmailService) *EmailHandler {
return &EmailHandler{
logger: logger.NewWrapper(logger.Get().With(zap.String("handler", "email"))),
emailService: emailService,
}
}
// VerifyEmail подтверждает email пользователя
func (h *EmailHandler) VerifyEmail(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling email verification request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
token := r.URL.Query().Get("token")
if token == "" {
h.logger.Warn("email verification failed - token is required")
utils.RespondWithError(w, http.StatusBadRequest, "Токен обязателен")
return
}
if err := h.emailService.VerifyEmail(token); err != nil {
h.logger.Error("email verification failed, expired",
zap.Error(err),
zap.String("token", token),
)
utils.RespondWithError(w, http.StatusBadRequest, "Неверный или просроченный токен")
return
}
h.logger.Info("email successfully verified",
zap.String("token", token),
)
utils.RespondWithJSON(w, http.StatusOK, map[string]string{
"message": "Email успешно подтвержден",
})
}
// RequestPasswordReset запрашивает сброс пароля
func (h *EmailHandler) RequestPasswordReset(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling password reset request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
var req models.PasswordResetRequest
if err := utils.DecodeJSONBody(w, r, &req); err != nil {
h.logger.Warn("password reset request failed - invalid request format",
zap.Error(err),
)
utils.RespondWithError(w, http.StatusBadRequest, "Неверный формат запроса")
return
}
if err := h.emailService.SendPasswordResetEmail(req.Email); err != nil {
h.logger.Error("password reset request failed",
zap.Error(err),
zap.String("email", req.Email),
)
// Для безопасности всегда возвращаем успех
}
h.logger.Info("password reset request processed",
zap.String("email", req.Email),
)
utils.RespondWithJSON(w, http.StatusOK, map[string]string{
"message": "Если email зарегистрирован, инструкции по восстановлению пароля будут отправлены",
})
}
// ConfirmPasswordReset подтверждает сброс пароля
func (h *EmailHandler) ConfirmPasswordReset(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling password reset confirmation request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
var req models.PasswordResetConfirm
if err := utils.DecodeJSONBody(w, r, &req); err != nil {
h.logger.Warn("password reset confirmation failed - invalid request format",
zap.Error(err),
)
utils.RespondWithError(w, http.StatusBadRequest, "Неверный формат запроса")
return
}
if err := h.emailService.ResetPassword(req.Token, req.Password); err != nil {
h.logger.Error("password reset confirmation failed",
zap.Error(err),
zap.String("token", req.Token),
)
utils.RespondWithError(w, http.StatusBadRequest, "Неверный или просроченный токен")
return
}
h.logger.Info("password successfully reset",
zap.String("token", req.Token),
)
utils.RespondWithJSON(w, http.StatusOK, map[string]string{
"message": "Пароль успешно изменен",
})
}
type NewsletterRequest struct {
Subject string `json:"subject" validate:"required"`
Content string `json:"content" validate:"required"`
}
// SendNewsletter отправляет рассылку новостей
func (h *EmailHandler) SendNewsletter(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling newsletter sending request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
var req NewsletterRequest
if err := utils.DecodeJSONBody(w, r, &req); err != nil {
h.logger.Warn("newsletter sending failed - invalid request format",
zap.Error(err),
)
utils.RespondWithError(w, http.StatusBadRequest, "Неверный формат запроса")
return
}
if err := h.emailService.SendNewsletterToSubscribers(req.Subject, req.Content); err != nil {
h.logger.Error("newsletter sending failed",
zap.Error(err),
zap.String("subject", req.Subject),
)
utils.RespondWithError(w, http.StatusInternalServerError, "Не удалось отправить рассылку")
return
}
h.logger.Info("newsletter sent successfully",
zap.String("subject", req.Subject),
)
utils.RespondWithJSON(w, http.StatusOK, map[string]string{
"message": "Рассылка отправлена подписчикам",
})
}
// ResendVerification повторно отправляет email верификации
func (h *EmailHandler) ResendVerification(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling resend verification request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
user, ok := middleware.GetUserFromContext(r.Context())
if !ok {
h.logger.Warn("resend verification failed - authentication required")
utils.RespondWithError(w, http.StatusUnauthorized, "Пользователь не авторизован")
return
}
// Получаем пользователя
userData, err := h.emailService.GetUserByID(user.ID)
if err != nil {
h.logger.Warn("resend verification failed - user not found",
zap.Uint("user_id", user.ID),
zap.Error(err),
)
utils.RespondWithError(w, http.StatusNotFound, "Пользователь не найден")
return
}
if userData.EmailVerified {
h.logger.Warn("resend verification failed - email already verified",
zap.Uint("user_id", user.ID),
zap.String("email", userData.Email),
)
utils.RespondWithError(w, http.StatusBadRequest, "Email уже подтвержден")
return
}
if err := h.emailService.SendVerificationEmail(userData.ID, userData.Email, userData.FirstName); err != nil {
h.logger.Error("resend verification failed",
zap.Error(err),
zap.Uint("user_id", user.ID),
zap.String("email", userData.Email),
)
utils.RespondWithError(w, http.StatusInternalServerError, "Не удалось отправить email подтверждения")
return
}
h.logger.Info("verification email resent successfully",
zap.Uint("user_id", user.ID),
zap.String("email", userData.Email),
)
utils.RespondWithJSON(w, http.StatusOK, map[string]string{
"message": "Email подтверждения отправлен повторно",
})
}
@@ -0,0 +1,495 @@
// handlers/event_handler.go
package handlers
import (
"api_bb/internal/models"
"api_bb/internal/service"
"api_bb/pkg/logger"
"api_bb/pkg/middleware"
"api_bb/pkg/utils"
"encoding/json"
"net/http"
"strconv"
"time"
"go.uber.org/zap"
)
type EventHandler struct {
logger logger.LoggerInterface
eventService service.EventService
}
func NewEventHandler(eventService service.EventService) *EventHandler {
return &EventHandler{
logger: logger.NewWrapper(logger.Get().With(zap.String("handler", "event"))),
eventService: eventService,
}
}
// CreateEventRequest - DTO для создания события
type CreateEventRequest struct {
Title string `json:"title" validate:"required,min=5,max=255"`
Description string `json:"description" validate:"required,min=10"`
Date time.Time `json:"date" validate:"required"`
Location string `json:"location" validate:"required,max=255"`
Type models.EventType `json:"type" validate:"required,oneof=race training social workshop"`
Distance string `json:"distance" validate:"max=50"`
MaxParticipants int `json:"max_participants" validate:"min=0"`
RegistrationOpen bool `json:"registration_open"`
Image string `json:"image" validate:"max=500"`
}
// UpdateEventRequest - DTO для обновления события
type UpdateEventRequest struct {
Title string `json:"title" validate:"required,min=5,max=255"`
Description string `json:"description" validate:"required,min=10"`
Date time.Time `json:"date" validate:"required"`
Location string `json:"location" validate:"required,max=255"`
Type models.EventType `json:"type" validate:"required,oneof=race training social workshop"`
Distance string `json:"distance" validate:"max=50"`
MaxParticipants int `json:"max_participants" validate:"min=0"`
RegistrationOpen bool `json:"registration_open"`
Image string `json:"image" validate:"max=500"`
}
// EventResponse - DTO для ответа с событием
type EventResponse struct {
ID uint `json:"id"`
Title string `json:"title"`
Description string `json:"description"`
Date time.Time `json:"date"`
Location string `json:"location"`
Type models.EventType `json:"type"`
Distance string `json:"distance"`
ParticipantsCount int `json:"participants_count"`
MaxParticipants int `json:"max_participants"`
RegistrationOpen bool `json:"registration_open"`
Image string `json:"image"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
// CreateEvent создает новое событие
func (h *EventHandler) CreateEvent(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling create event request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Проверяем аутентификацию
user, ok := middleware.GetUserFromContext(r.Context())
if !ok {
h.logger.Warn("create event failed - authentication required")
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
// Проверяем права доступа (только админы могут создавать события)
if user.Role != "admin" {
h.logger.Warn("create event failed - insufficient permissions",
zap.Uint("user_id", user.ID),
zap.String("user_role", user.Role),
)
utils.RespondWithError(w, http.StatusForbidden, "Insufficient permissions")
return
}
var req CreateEventRequest
if err := utils.DecodeJSONBody(w, r, &req); err != nil {
h.logger.Error("failed to decode request body", zap.Error(err))
utils.RespondWithError(w, http.StatusBadRequest, "Invalid request payload: "+err.Error())
return
}
// Валидация
if err := utils.ValidateStruct(req); err != nil {
h.logger.Warn("validation failed for create event", zap.Error(err))
utils.RespondWithValidationError(w, err)
return
}
// Создаем модель события
event := &models.Event{
Title: req.Title,
Description: req.Description,
Date: req.Date,
Location: req.Location,
Type: req.Type,
Distance: req.Distance,
MaxParticipants: req.MaxParticipants,
RegistrationOpen: req.RegistrationOpen,
Image: req.Image,
}
if err := h.eventService.CreateEvent(event); err != nil {
h.logger.Error("failed to create event", zap.Error(err))
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to create event: "+err.Error())
return
}
h.logger.Info("event created successfully",
zap.Uint("event_id", event.ID),
zap.String("title", event.Title),
)
utils.RespondWithJSON(w, http.StatusCreated, map[string]interface{}{
"message": "Event created successfully",
"event": toEventResponse(event),
})
}
// GetEvent возвращает событие по ID
func (h *EventHandler) GetEvent(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling get event request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Извлекаем ID события из URL параметров
eventID, err := strconv.ParseUint(r.PathValue("id"), 10, 32)
if err != nil {
h.logger.Warn("invalid event ID", zap.Error(err))
utils.RespondWithError(w, http.StatusBadRequest, "Invalid event ID")
return
}
event, err := h.eventService.GetEventByID(uint(eventID))
if err != nil {
h.logger.Warn("event not found",
zap.Uint("event_id", uint(eventID)),
zap.Error(err),
)
utils.RespondWithError(w, http.StatusNotFound, "Event not found")
return
}
h.logger.Info("event retrieved successfully",
zap.Uint("event_id", uint(eventID)),
zap.String("title", event.Title),
)
utils.RespondWithJSON(w, http.StatusOK, toEventResponse(event))
}
// GetAllEvents возвращает все события
func (h *EventHandler) GetAllEvents(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling get all events request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
events, err := h.eventService.GetAllEvents()
if err != nil {
h.logger.Error("failed to get events", zap.Error(err))
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get events: "+err.Error())
return
}
// Преобразуем в response формат
var eventResponses []EventResponse
for _, event := range events {
eventResponses = append(eventResponses, toEventResponse(&event))
}
h.logger.Info("events list retrieved successfully",
zap.Int("events_count", len(eventResponses)),
)
utils.RespondWithJSON(w, http.StatusOK, eventResponses)
}
// UpdateEvent обновляет событие
func (h *EventHandler) UpdateEvent(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling update event request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Проверяем аутентификацию и права
user, ok := middleware.GetUserFromContext(r.Context())
if !ok {
h.logger.Warn("update event failed - authentication required")
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
if user.Role != "admin" {
h.logger.Warn("update event failed - insufficient permissions",
zap.Uint("user_id", user.ID),
zap.String("user_role", user.Role),
)
utils.RespondWithError(w, http.StatusForbidden, "Insufficient permissions")
return
}
// Извлекаем ID события
eventID, err := strconv.ParseUint(r.PathValue("id"), 10, 32)
if err != nil {
h.logger.Warn("invalid event ID", zap.Error(err))
utils.RespondWithError(w, http.StatusBadRequest, "Invalid event ID")
return
}
var req UpdateEventRequest
if err := utils.DecodeJSONBody(w, r, &req); err != nil {
h.logger.Error("failed to decode request body", zap.Error(err))
utils.RespondWithError(w, http.StatusBadRequest, "Invalid request payload: "+err.Error())
return
}
// Валидация
if err := utils.ValidateStruct(req); err != nil {
h.logger.Warn("validation failed for update event", zap.Error(err))
utils.RespondWithValidationError(w, err)
return
}
// Создаем модель события для обновления
event := &models.Event{
ID: uint(eventID),
Title: req.Title,
Description: req.Description,
Date: req.Date,
Location: req.Location,
Type: req.Type,
Distance: req.Distance,
MaxParticipants: req.MaxParticipants,
RegistrationOpen: req.RegistrationOpen,
Image: req.Image,
}
if err := h.eventService.UpdateEvent(event); err != nil {
h.logger.Error("failed to update event",
zap.Uint("event_id", uint(eventID)),
zap.Error(err),
)
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to update event: "+err.Error())
return
}
h.logger.Info("event updated successfully",
zap.Uint("event_id", uint(eventID)),
zap.String("title", event.Title),
)
utils.RespondWithJSON(w, http.StatusOK, map[string]interface{}{
"message": "Event updated successfully",
"event": toEventResponse(event),
})
}
// DeleteEvent удаляет событие
func (h *EventHandler) DeleteEvent(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling delete event request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Проверяем аутентификацию и права
user, ok := middleware.GetUserFromContext(r.Context())
if !ok {
h.logger.Warn("delete event failed - authentication required")
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
if user.Role != "admin" {
h.logger.Warn("delete event failed - insufficient permissions",
zap.Uint("user_id", user.ID),
zap.String("user_role", user.Role),
)
utils.RespondWithError(w, http.StatusForbidden, "Insufficient permissions")
return
}
// Извлекаем ID события
eventID, err := strconv.ParseUint(r.PathValue("id"), 10, 32)
if err != nil {
h.logger.Warn("invalid event ID", zap.Error(err))
utils.RespondWithError(w, http.StatusBadRequest, "Invalid event ID")
return
}
if err := h.eventService.DeleteEvent(uint(eventID)); err != nil {
h.logger.Error("failed to delete event",
zap.Uint("event_id", uint(eventID)),
zap.Error(err),
)
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to delete event: "+err.Error())
return
}
h.logger.Info("event deleted successfully",
zap.Uint("event_id", uint(eventID)),
)
utils.RespondWithJSON(w, http.StatusOK, map[string]string{
"message": "Event deleted successfully",
})
}
// GetEventsByType возвращает события по типу
func (h *EventHandler) GetEventsByType(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling get events by type request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
eventType := models.EventType(r.PathValue("type"))
// Валидация типа события
validTypes := []models.EventType{"race", "training", "social", "workshop"}
if !isValidEventType(eventType, validTypes) {
h.logger.Warn("invalid event type", zap.String("event_type", string(eventType)))
utils.RespondWithError(w, http.StatusBadRequest, "Invalid event type")
return
}
events, err := h.eventService.GetEventsByType(eventType)
if err != nil {
h.logger.Error("failed to get events by type",
zap.String("event_type", string(eventType)),
zap.Error(err),
)
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get events: "+err.Error())
return
}
var eventResponses []EventResponse
for _, event := range events {
eventResponses = append(eventResponses, toEventResponse(&event))
}
h.logger.Info("events by type retrieved successfully",
zap.String("event_type", string(eventType)),
zap.Int("events_count", len(eventResponses)),
)
utils.RespondWithJSON(w, http.StatusOK, eventResponses)
}
// GetUpcomingEvents возвращает предстоящие события
func (h *EventHandler) GetUpcomingEvents(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling get upcoming events request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
events, err := h.eventService.GetUpcomingEvents()
if err != nil {
h.logger.Error("failed to get upcoming events", zap.Error(err))
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get upcoming events: "+err.Error())
return
}
var eventResponses []EventResponse
for _, event := range events {
eventResponses = append(eventResponses, toEventResponse(&event))
}
h.logger.Info("upcoming events retrieved successfully",
zap.Int("events_count", len(eventResponses)),
)
utils.RespondWithJSON(w, http.StatusOK, eventResponses)
}
// ToggleRegistrationStatus переключает статус регистрации
func (h *EventHandler) ToggleRegistrationStatus(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling toggle registration status request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Проверяем аутентификацию и права
user, ok := middleware.GetUserFromContext(r.Context())
if !ok {
h.logger.Warn("toggle registration status failed - authentication required")
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
if user.Role != "admin" {
h.logger.Warn("toggle registration status failed - insufficient permissions",
zap.Uint("user_id", user.ID),
zap.String("user_role", user.Role),
)
utils.RespondWithError(w, http.StatusForbidden, "Insufficient permissions")
return
}
// Извлекаем ID события
eventID, err := strconv.ParseUint(r.PathValue("id"), 10, 32)
if err != nil {
h.logger.Warn("invalid event ID", zap.Error(err))
utils.RespondWithError(w, http.StatusBadRequest, "Invalid event ID")
return
}
var req struct {
RegistrationOpen bool `json:"registration_open" validate:"required"`
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
h.logger.Error("failed to decode request body", zap.Error(err))
utils.RespondWithError(w, http.StatusBadRequest, "Invalid request payload")
return
}
if err := h.eventService.ToggleRegistrationStatus(uint(eventID), req.RegistrationOpen); err != nil {
h.logger.Error("failed to toggle registration status",
zap.Uint("event_id", uint(eventID)),
zap.Bool("registration_open", req.RegistrationOpen),
zap.Error(err),
)
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to toggle registration status: "+err.Error())
return
}
h.logger.Info("registration status toggled successfully",
zap.Uint("event_id", uint(eventID)),
zap.Bool("registration_open", req.RegistrationOpen),
)
utils.RespondWithJSON(w, http.StatusOK, map[string]string{
"message": "Registration status updated successfully",
})
}
// toEventResponse преобразует модель события в response DTO
func toEventResponse(event *models.Event) EventResponse {
return EventResponse{
ID: event.ID,
Title: event.Title,
Description: event.Description,
Date: event.Date,
Location: event.Location,
Type: event.Type,
Distance: event.Distance,
ParticipantsCount: event.ParticipantsCount,
MaxParticipants: event.MaxParticipants,
RegistrationOpen: event.RegistrationOpen,
Image: event.Image,
CreatedAt: event.CreatedAt,
UpdatedAt: event.UpdatedAt,
}
}
// isValidEventType проверяет валидность типа события
func isValidEventType(eventType models.EventType, validTypes []models.EventType) bool {
for _, validType := range validTypes {
if eventType == validType {
return true
}
}
return false
}
@@ -0,0 +1,527 @@
// handlers/event_registration_handler.go
package handlers
import (
"api_bb/internal/models"
"api_bb/internal/service"
"api_bb/pkg/logger"
"api_bb/pkg/middleware"
"api_bb/pkg/utils"
"encoding/json"
"net/http"
"strconv"
"go.uber.org/zap"
)
type EventRegistrationHandler struct {
logger logger.LoggerInterface
registrationService service.EventRegistrationService
}
func NewEventRegistrationHandler(registrationService service.EventRegistrationService) *EventRegistrationHandler {
return &EventRegistrationHandler{
logger: logger.NewWrapper(logger.Get().With(zap.String("handler", "event_registration"))),
registrationService: registrationService,
}
}
// RegisterForEventRequest - DTO для регистрации на событие
type RegisterForEventRequest struct {
EventID uint `json:"event_id" validate:"required"`
Notes string `json:"notes" validate:"max=500"`
}
// UpdateRegistrationRequest - DTO для обновления регистрации
type UpdateRegistrationRequest struct {
Notes string `json:"notes" validate:"max=500"`
}
// RegistrationResponse - DTO для ответа с регистрацией
type RegistrationResponse struct {
ID uint `json:"id"`
UserID uint `json:"user_id"`
EventID uint `json:"event_id"`
Status string `json:"status"`
Notes string `json:"notes"`
ResultTime string `json:"result_time"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
Event EventResponse `json:"event,omitempty"`
}
// RegisterForEvent регистрирует пользователя на событие
func (h *EventRegistrationHandler) RegisterForEvent(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling register for event request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Проверяем аутентификацию
user, ok := middleware.GetUserFromContext(r.Context())
if !ok {
h.logger.Warn("register for event failed - authentication required")
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
var req RegisterForEventRequest
if err := utils.DecodeJSONBody(w, r, &req); err != nil {
h.logger.Error("failed to decode request body", zap.Error(err))
utils.RespondWithError(w, http.StatusBadRequest, "Invalid request payload: "+err.Error())
return
}
// Валидация
if err := utils.ValidateStruct(req); err != nil {
h.logger.Warn("validation failed for register for event", zap.Error(err))
utils.RespondWithValidationError(w, err)
return
}
// Создаем модель регистрации
registration := &models.EventRegistration{
UserID: user.ID,
EventID: req.EventID,
Status: "pending",
Notes: req.Notes,
}
if err := h.registrationService.RegisterForEvent(registration); err != nil {
h.logger.Error("failed to register for event",
zap.Uint("user_id", user.ID),
zap.Uint("event_id", req.EventID),
zap.Error(err),
)
statusCode := http.StatusInternalServerError
if err.Error() == "event not found" {
statusCode = http.StatusNotFound
} else if err.Error() == "user already registered for this event" {
statusCode = http.StatusConflict
} else if err.Error() == "registration is closed for this event" {
statusCode = http.StatusForbidden
} else if err.Error() == "event is full" {
statusCode = http.StatusConflict
}
utils.RespondWithError(w, statusCode, "Failed to register for event: "+err.Error())
return
}
h.logger.Info("user registered for event successfully",
zap.Uint("user_id", user.ID),
zap.Uint("event_id", req.EventID),
zap.Uint("registration_id", registration.ID),
)
utils.RespondWithJSON(w, http.StatusCreated, map[string]interface{}{
"message": "Successfully registered for event",
"registration": toRegistrationResponse(registration),
})
}
// GetRegistration возвращает регистрацию по ID
func (h *EventRegistrationHandler) GetRegistration(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling get registration request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Проверяем аутентификацию
user, ok := middleware.GetUserFromContext(r.Context())
if !ok {
h.logger.Warn("get registration failed - authentication required")
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
// Извлекаем ID регистрации
registrationID, err := strconv.ParseUint(r.PathValue("id"), 10, 32)
if err != nil {
h.logger.Warn("invalid registration ID", zap.Error(err))
utils.RespondWithError(w, http.StatusBadRequest, "Invalid registration ID")
return
}
registration, err := h.registrationService.GetRegistrationByID(uint(registrationID))
if err != nil {
h.logger.Warn("registration not found",
zap.Uint("registration_id", uint(registrationID)),
zap.Error(err),
)
utils.RespondWithError(w, http.StatusNotFound, "Registration not found")
return
}
// Проверяем права доступа (пользователь может видеть только свои регистрации, админ - все)
if user.Role != "admin" && registration.UserID != user.ID {
h.logger.Warn("access denied to registration",
zap.Uint("user_id", user.ID),
zap.Uint("registration_user_id", registration.UserID),
)
utils.RespondWithError(w, http.StatusForbidden, "Access denied")
return
}
h.logger.Info("registration retrieved successfully",
zap.Uint("registration_id", uint(registrationID)),
zap.Uint("user_id", user.ID),
)
utils.RespondWithJSON(w, http.StatusOK, toRegistrationResponse(registration))
}
// GetUserRegistrations возвращает все регистрации пользователя
func (h *EventRegistrationHandler) GetUserRegistrations(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling get user registrations request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Проверяем аутентификацию
user, ok := middleware.GetUserFromContext(r.Context())
if !ok {
h.logger.Warn("get user registrations failed - authentication required")
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
registrations, err := h.registrationService.GetRegistrationsByUserID(user.ID)
if err != nil {
h.logger.Error("failed to get user registrations",
zap.Uint("user_id", user.ID),
zap.Error(err),
)
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get registrations: "+err.Error())
return
}
var registrationResponses []RegistrationResponse
for _, registration := range registrations {
registrationResponses = append(registrationResponses, toRegistrationResponse(&registration))
}
h.logger.Info("user registrations retrieved successfully",
zap.Uint("user_id", user.ID),
zap.Int("registrations_count", len(registrationResponses)),
)
utils.RespondWithJSON(w, http.StatusOK, registrationResponses)
}
// GetEventRegistrations возвращает все регистрации на событие
func (h *EventRegistrationHandler) GetEventRegistrations(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling get event registrations request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Проверяем аутентификацию и права (только админы могут видеть все регистрации на событие)
user, ok := middleware.GetUserFromContext(r.Context())
if !ok {
h.logger.Warn("get event registrations failed - authentication required")
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
if user.Role != "admin" {
h.logger.Warn("get event registrations failed - insufficient permissions",
zap.Uint("user_id", user.ID),
zap.String("user_role", user.Role),
)
utils.RespondWithError(w, http.StatusForbidden, "Insufficient permissions")
return
}
// Извлекаем ID события
eventID, err := strconv.ParseUint(r.PathValue("eventId"), 10, 32)
if err != nil {
h.logger.Warn("invalid event ID", zap.Error(err))
utils.RespondWithError(w, http.StatusBadRequest, "Invalid event ID")
return
}
registrations, err := h.registrationService.GetRegistrationsByEventID(uint(eventID))
if err != nil {
h.logger.Error("failed to get event registrations",
zap.Uint("event_id", uint(eventID)),
zap.Error(err),
)
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get registrations: "+err.Error())
return
}
var registrationResponses []RegistrationResponse
for _, registration := range registrations {
registrationResponses = append(registrationResponses, toRegistrationResponse(&registration))
}
h.logger.Info("event registrations retrieved successfully",
zap.Uint("event_id", uint(eventID)),
zap.Int("registrations_count", len(registrationResponses)),
)
utils.RespondWithJSON(w, http.StatusOK, registrationResponses)
}
// CancelRegistration отменяет регистрацию
func (h *EventRegistrationHandler) CancelRegistration(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling cancel registration request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Проверяем аутентификацию
user, ok := middleware.GetUserFromContext(r.Context())
if !ok {
h.logger.Warn("cancel registration failed - authentication required")
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
// Извлекаем ID регистрации
registrationID, err := strconv.ParseUint(r.PathValue("id"), 10, 32)
if err != nil {
h.logger.Warn("invalid registration ID", zap.Error(err))
utils.RespondWithError(w, http.StatusBadRequest, "Invalid registration ID")
return
}
// Проверяем права доступа
registration, err := h.registrationService.GetRegistrationByID(uint(registrationID))
if err != nil {
h.logger.Warn("registration not found for cancellation",
zap.Uint("registration_id", uint(registrationID)),
zap.Error(err),
)
utils.RespondWithError(w, http.StatusNotFound, "Registration not found")
return
}
if user.Role != "admin" && registration.UserID != user.ID {
h.logger.Warn("access denied to cancel registration",
zap.Uint("user_id", user.ID),
zap.Uint("registration_user_id", registration.UserID),
)
utils.RespondWithError(w, http.StatusForbidden, "Access denied")
return
}
if err := h.registrationService.CancelRegistration(uint(registrationID)); err != nil {
h.logger.Error("failed to cancel registration",
zap.Uint("registration_id", uint(registrationID)),
zap.Error(err),
)
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to cancel registration: "+err.Error())
return
}
h.logger.Info("registration cancelled successfully",
zap.Uint("registration_id", uint(registrationID)),
zap.Uint("user_id", user.ID),
)
utils.RespondWithJSON(w, http.StatusOK, map[string]string{
"message": "Registration cancelled successfully",
})
}
// UpdateRegistrationStatus обновляет статус регистрации
func (h *EventRegistrationHandler) UpdateRegistrationStatus(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling update registration status request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Проверяем аутентификацию и права (только админы)
user, ok := middleware.GetUserFromContext(r.Context())
if !ok {
h.logger.Warn("update registration status failed - authentication required")
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
if user.Role != "admin" {
h.logger.Warn("update registration status failed - insufficient permissions",
zap.Uint("user_id", user.ID),
zap.String("user_role", user.Role),
)
utils.RespondWithError(w, http.StatusForbidden, "Insufficient permissions")
return
}
// Извлекаем ID регистрации
registrationID, err := strconv.ParseUint(r.PathValue("id"), 10, 32)
if err != nil {
h.logger.Warn("invalid registration ID", zap.Error(err))
utils.RespondWithError(w, http.StatusBadRequest, "Invalid registration ID")
return
}
var req struct {
Status string `json:"status" validate:"required,oneof=pending confirmed cancelled completed"`
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
h.logger.Error("failed to decode request body", zap.Error(err))
utils.RespondWithError(w, http.StatusBadRequest, "Invalid request payload")
return
}
if err := utils.ValidateStruct(req); err != nil {
h.logger.Warn("validation failed for update registration status", zap.Error(err))
utils.RespondWithValidationError(w, err)
return
}
if err := h.registrationService.UpdateRegistrationStatus(uint(registrationID), req.Status); err != nil {
h.logger.Error("failed to update registration status",
zap.Uint("registration_id", uint(registrationID)),
zap.String("status", req.Status),
zap.Error(err),
)
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to update registration status: "+err.Error())
return
}
h.logger.Info("registration status updated successfully",
zap.Uint("registration_id", uint(registrationID)),
zap.String("status", req.Status),
)
utils.RespondWithJSON(w, http.StatusOK, map[string]string{
"message": "Registration status updated successfully",
})
}
// UpdateResultTime обновляет результат забега
func (h *EventRegistrationHandler) UpdateResultTime(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling update result time request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Проверяем аутентификацию и права (только админы)
user, ok := middleware.GetUserFromContext(r.Context())
if !ok {
h.logger.Warn("update result time failed - authentication required")
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
if user.Role != "admin" {
h.logger.Warn("update result time failed - insufficient permissions",
zap.Uint("user_id", user.ID),
zap.String("user_role", user.Role),
)
utils.RespondWithError(w, http.StatusForbidden, "Insufficient permissions")
return
}
// Извлекаем ID регистрации
registrationID, err := strconv.ParseUint(r.PathValue("id"), 10, 32)
if err != nil {
h.logger.Warn("invalid registration ID", zap.Error(err))
utils.RespondWithError(w, http.StatusBadRequest, "Invalid registration ID")
return
}
var req struct {
ResultTime string `json:"result_time" validate:"required,max=20"`
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
h.logger.Error("failed to decode request body", zap.Error(err))
utils.RespondWithError(w, http.StatusBadRequest, "Invalid request payload")
return
}
if err := utils.ValidateStruct(req); err != nil {
h.logger.Warn("validation failed for update result time", zap.Error(err))
utils.RespondWithValidationError(w, err)
return
}
if err := h.registrationService.UpdateResultTime(uint(registrationID), req.ResultTime); err != nil {
h.logger.Error("failed to update result time",
zap.Uint("registration_id", uint(registrationID)),
zap.String("result_time", req.ResultTime),
zap.Error(err),
)
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to update result time: "+err.Error())
return
}
h.logger.Info("result time updated successfully",
zap.Uint("registration_id", uint(registrationID)),
zap.String("result_time", req.ResultTime),
)
utils.RespondWithJSON(w, http.StatusOK, map[string]string{
"message": "Result time updated successfully",
})
}
// CheckEventAvailability проверяет доступность мест на событии
func (h *EventRegistrationHandler) CheckEventAvailability(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling check event availability request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Извлекаем ID события
eventID, err := strconv.ParseUint(r.PathValue("eventId"), 10, 32)
if err != nil {
h.logger.Warn("invalid event ID", zap.Error(err))
utils.RespondWithError(w, http.StatusBadRequest, "Invalid event ID")
return
}
available, err := h.registrationService.CheckEventAvailability(uint(eventID))
if err != nil {
h.logger.Error("failed to check event availability",
zap.Uint("event_id", uint(eventID)),
zap.Error(err),
)
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to check availability: "+err.Error())
return
}
h.logger.Info("event availability checked successfully",
zap.Uint("event_id", uint(eventID)),
zap.Bool("available", available),
)
utils.RespondWithJSON(w, http.StatusOK, map[string]interface{}{
"event_id": eventID,
"available": available,
})
}
// toRegistrationResponse преобразует модель регистрации в response DTO
func toRegistrationResponse(registration *models.EventRegistration) RegistrationResponse {
response := RegistrationResponse{
ID: registration.ID,
UserID: registration.UserID,
EventID: registration.EventID,
Status: registration.Status,
Notes: registration.Notes,
ResultTime: registration.ResultTime,
CreatedAt: registration.CreatedAt.Format("2006-01-02 15:04:05"),
UpdatedAt: registration.UpdatedAt.Format("2006-01-02 15:04:05"),
}
// Включаем информацию о событии, если она загружена
if registration.Event != nil {
response.Event = toEventResponse(registration.Event)
}
return response
}
@@ -0,0 +1,23 @@
package handlers
import (
"api_bb/internal/models"
)
// Общая функция для преобразования User в UserResponse
func toUserResponse(user *models.User) UserResponse {
return UserResponse{
ID: user.ID,
Email: user.Email,
FirstName: user.FirstName,
LastName: user.LastName,
Avatar: user.Avatar,
Phone: user.Phone,
Experience: user.Experience,
Goals: user.Goals,
Newsletter: user.Newsletter,
Role: user.Role,
CreatedAt: user.CreatedAt,
UpdatedAt: user.UpdatedAt,
}
}
@@ -0,0 +1,165 @@
// handlers/handlers.go
package handlers
import (
"api_bb/internal/config"
"api_bb/internal/repository"
"api_bb/internal/service"
"api_bb/pkg/email"
"api_bb/pkg/logger"
"go.uber.org/zap"
"gorm.io/gorm"
)
type Handler struct {
healthHandler *HealthHandler
authHandler *AuthHandler
userHandler *UserHandler
avatarHandler *AvatarHandler
newsHandler *NewsHandler
reviewHandler *ReviewHandler
userStatsHandler *UserStatsHandler
userWorkoutHandler *UserWorkoutHandler
userAchievementHandler *UserAchievementHandler
eventHandler *EventHandler
eventRegistrationHandler *EventRegistrationHandler
personalBestHandler *PersonalBestHandler
trainingPlanHandler *TrainingPlanHandler
emailHandler *EmailHandler
// Здесь будут добавлены другие обработчики
// userHandler *UserHandler
// eventHandler *EventHandler
// reviewHandler *ReviewHandler
}
func NewHandler(db *gorm.DB, cfg *config.Config) *Handler {
// Инициализация репозиториев
userRepo := repository.NewUserRepository(db)
newsRepo := repository.NewNewsRepository(db)
commentRepo := repository.NewCommentRepository(db)
reviewRepo := repository.NewReviewRepository(db)
userStatsRepo := repository.NewUserStatsRepository(db)
userWorkoutRepo := repository.NewWorkoutRepository(db)
userAchievemenRepo := repository.NewAchievementRepository(db)
eventRepo := repository.NewEventRepository(db)
eventRegistrationRepo := repository.NewEventRegistrationRepository(db)
personalBestRepo := repository.NewPersonalBestRepository(db)
trainingPlanRepo := repository.NewTrainingPlanRepository(db)
emailRepo := repository.NewEmailRepository(db)
// Initialize logger
baseLogger := logger.NewWrapper(logger.Get()) // Создаем базовый логгер
// getConfig
emailSender, err := email.NewService(config.Load())
if err != nil {
baseLogger.Info("error to load config", zap.Error(err))
}
// Инициализация сервисов
jwtService := service.NewJWTService(cfg.JWTSecret)
authService := service.NewAuthService(userRepo, jwtService, baseLogger)
userService := service.NewUserService(userRepo, jwtService, baseLogger)
avatarService := service.NewAvatarService(userRepo, baseLogger)
newsService := service.NewNewsService(newsRepo, commentRepo, baseLogger)
reviewService := service.NewReviewService(reviewRepo, baseLogger)
userStatsService := service.NewUserStatsService(userStatsRepo)
userWorkoutService := service.NewWorkoutService(userWorkoutRepo)
achievementService := service.NewAchievementService(userAchievemenRepo)
eventRegistrationService := service.NewEventRegistrationService(eventRegistrationRepo, eventRepo, baseLogger)
eventService := service.NewEventService(eventRepo, eventRegistrationRepo, baseLogger)
personalBestService := service.NewPersonalBestService(personalBestRepo, userStatsService)
trainingPlanService := service.NewTrainingPlanService(*trainingPlanRepo)
emailService := service.NewEmailService(*emailRepo, userRepo, *emailSender)
// Инициализация обработчиков
healthHandler := NewHealthHandler()
authHandler := NewAuthHandler(authService, jwtService, emailService)
userHandler := NewUserHandler(&userService)
newsHandler := NewNewsHandler(newsService, baseLogger)
avatarHandler := NewAvatarHandler(avatarService)
reviewHandler := NewReviewHandler(reviewService, baseLogger)
userStatsHandler := NewUserStatsHandler(userStatsService)
userWorkoutHandler := NewUserWorkoutHandler(userWorkoutService)
userAchievementHandler := NewUserAchievementHandler(*achievementService)
eventHandler := NewEventHandler(eventService)
eventRegistrationHandler := NewEventRegistrationHandler(eventRegistrationService)
personalBestHandler := NewPersonalBestHandler(*personalBestService)
trainingPlanHandler := NewTrainingPlanHandler(trainingPlanService)
emailHandler := NewEmailHandler(&emailService)
return &Handler{
healthHandler: healthHandler,
authHandler: authHandler,
userHandler: userHandler,
newsHandler: newsHandler,
avatarHandler: avatarHandler,
reviewHandler: reviewHandler,
userStatsHandler: userStatsHandler,
userWorkoutHandler: userWorkoutHandler,
userAchievementHandler: userAchievementHandler,
eventHandler: eventHandler,
eventRegistrationHandler: eventRegistrationHandler,
personalBestHandler: personalBestHandler,
trainingPlanHandler: trainingPlanHandler,
emailHandler: emailHandler,
}
}
// Геттеры для обработчиков (опционально, для удобства)
func (h *Handler) EmailHandler() *EmailHandler {
return h.emailHandler
}
func (h *Handler) TrainingPlanHandler() *TrainingPlanHandler {
return h.trainingPlanHandler
}
func (h *Handler) PersonalBestHandler() *PersonalBestHandler {
return h.personalBestHandler
}
func (h *Handler) EventHandler() *EventHandler {
return h.eventHandler
}
func (h *Handler) EventRegistrationHandler() *EventRegistrationHandler {
return h.eventRegistrationHandler
}
func (h *Handler) HealthHandler() *HealthHandler {
return h.healthHandler
}
func (h *Handler) AuthHandler() *AuthHandler {
return h.authHandler
}
func (h *Handler) UserHandler() *UserHandler {
return h.userHandler
}
func (h *Handler) AvatarHandler() *AvatarHandler {
return h.avatarHandler
}
func (h *Handler) NewsHandler() *NewsHandler {
return h.newsHandler
}
func (h *Handler) ReviewHandler() *ReviewHandler {
return h.reviewHandler
}
func (h *Handler) UserStatsHandler() *UserStatsHandler {
return h.userStatsHandler
}
func (h *Handler) UserWorkoutHandler() *UserWorkoutHandler {
return h.userWorkoutHandler
}
func (h *Handler) UserAchievementHandler() *UserAchievementHandler {
return h.userAchievementHandler
}
@@ -0,0 +1,31 @@
package handlers
import (
"net/http"
"api_bb/pkg/utils"
)
type HealthHandler struct{}
func NewHealthHandler() *HealthHandler {
return &HealthHandler{}
}
func (h *HealthHandler) HealthCheck(w http.ResponseWriter, r *http.Request) {
response := map[string]string{
"status": "ok",
"message": "Service is healthy",
}
utils.RespondWithJSON(w, http.StatusOK, response)
}
func (h *HealthHandler) Check(w http.ResponseWriter, r *http.Request) {
response := map[string]string{
"status": "ok",
"message": "API is working",
}
utils.RespondWithJSON(w, http.StatusOK, response)
}
@@ -0,0 +1,432 @@
package handlers
import (
"api_bb/internal/models"
"api_bb/internal/service"
"api_bb/pkg/logger"
"api_bb/pkg/middleware"
"api_bb/pkg/utils"
"net/http"
"strconv"
"github.com/go-chi/chi/v5"
"github.com/go-playground/validator/v10"
"go.uber.org/zap"
)
type NewsHandler struct {
newsService service.NewsService
logger logger.LoggerInterface
validator *validator.Validate
}
func NewNewsHandler(newsService service.NewsService, log logger.LoggerInterface) *NewsHandler {
return &NewsHandler{
newsService: newsService,
logger: log,
validator: validator.New(),
}
}
// GetNews возвращает список новостей с пагинацией и фильтрацией
func (h *NewsHandler) GetNews(w http.ResponseWriter, r *http.Request) {
h.logger.Debug("Start GetNews Method")
limit, _ := strconv.Atoi(r.URL.Query().Get("limit"))
offset, _ := strconv.Atoi(r.URL.Query().Get("offset"))
category := r.URL.Query().Get("category")
h.logger.Debug("GetNews parameters",
zap.Int("limit", limit),
zap.Int("offset", offset),
zap.String("category", category),
)
if limit == 0 {
limit = 10
}
if limit > 50 {
limit = 50
}
news, total, err := h.newsService.GetAllNews(limit, offset, category)
if err != nil {
h.logger.Error("Failed to get news", zap.Error(err))
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get news")
return
}
h.logger.Debug("Successfully retrieved news",
zap.Int("count", len(news)),
zap.Int("total", int(total)),
)
h.logger.Debug("End GetNews Method")
utils.RespondWithJSON(w, http.StatusOK, map[string]interface{}{
"news": news,
"total": total,
"limit": limit,
"offset": offset,
})
}
// GetNewsByID возвращает конкретную новость
func (h *NewsHandler) GetNewsByID(w http.ResponseWriter, r *http.Request) {
h.logger.Debug("Start GetNewsByID Method")
idStr := chi.URLParam(r, "id")
h.logger.Debug("GetNewsByID parameters", zap.String("id", idStr))
id, err := strconv.ParseUint(idStr, 10, 32)
if err != nil {
h.logger.Warn("Invalid news ID", zap.String("id", idStr), zap.Error(err))
utils.RespondWithError(w, http.StatusBadRequest, "Invalid news ID")
return
}
news, err := h.newsService.GetNewsByID(uint(id))
if err != nil {
h.logger.Warn("News not found", zap.Uint("id", uint(id)), zap.Error(err))
utils.RespondWithError(w, http.StatusNotFound, "News not found")
return
}
h.logger.Debug("Successfully retrieved news by ID", zap.Uint("id", uint(id)))
h.logger.Debug("End GetNewsByID Method")
utils.RespondWithJSON(w, http.StatusOK, news)
}
// CreateNews создает новую новость
func (h *NewsHandler) CreateNews(w http.ResponseWriter, r *http.Request) {
h.logger.Debug("Start CreateNews Method")
userID, ok := r.Context().Value(middleware.UserIDKey).(uint)
h.logger.Debug("CreateNews user context", zap.Uint("userID", userID), zap.Bool("ok", ok))
if !ok {
h.logger.Warn("Failed to get userID from context in CreateNews",
zap.String("path", r.URL.Path),
zap.String("method", r.Method),
zap.String("remote_addr", r.RemoteAddr),
)
utils.RespondWithError(w, http.StatusUnauthorized, "Unauthorized")
return
}
var req models.CreateNewsRequest
if err := utils.DecodeJSONBody(w, r, &req); err != nil {
h.logger.Warn("Invalid request body in CreateNews", zap.Error(err))
utils.RespondWithError(w, http.StatusBadRequest, "Invalid request body")
return
}
h.logger.Debug("CreateNews request data",
zap.String("title", req.Title),
zap.String("category", string(req.Category)),
)
if err := h.validator.Struct(req); err != nil {
h.logger.Warn("Validation failed in CreateNews", zap.Error(err))
utils.RespondWithError(w, http.StatusBadRequest, "Validation failed: "+err.Error())
return
}
news, err := h.newsService.CreateNews(req, userID)
if err != nil {
h.logger.Error("Failed to create news", zap.Error(err))
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to create news")
return
}
h.logger.Info("Successfully created news",
zap.Uint("newsID", news.ID),
zap.Uint("userID", userID),
)
h.logger.Debug("End CreateNews Method")
utils.RespondWithJSON(w, http.StatusCreated, news)
}
// UpdateNews обновляет новость
func (h *NewsHandler) UpdateNews(w http.ResponseWriter, r *http.Request) {
h.logger.Debug("Start UpdateNews Method")
userID, ok := r.Context().Value(middleware.UserIDKey).(uint)
h.logger.Debug("UpdateNews user context", zap.Uint("userID", userID), zap.Bool("ok", ok))
if !ok {
h.logger.Warn("Failed to get userID from context in UpdateNews")
utils.RespondWithError(w, http.StatusUnauthorized, "Unauthorized")
return
}
idStr := chi.URLParam(r, "id")
h.logger.Debug("UpdateNews parameters", zap.String("id", idStr))
id, err := strconv.ParseUint(idStr, 10, 32)
if err != nil {
h.logger.Warn("Invalid news ID in UpdateNews", zap.String("id", idStr), zap.Error(err))
utils.RespondWithError(w, http.StatusBadRequest, "Invalid news ID")
return
}
var req models.UpdateNewsRequest
if err := utils.DecodeJSONBody(w, r, &req); err != nil {
h.logger.Warn("Invalid request body in UpdateNews", zap.Error(err))
utils.RespondWithError(w, http.StatusBadRequest, "Invalid request body")
return
}
h.logger.Debug("UpdateNews request data",
zap.String("title", req.Title),
zap.String("category", string(req.Category)),
)
if err := h.validator.Struct(req); err != nil {
h.logger.Warn("Validation failed in UpdateNews", zap.Error(err))
utils.RespondWithError(w, http.StatusBadRequest, "Validation failed: "+err.Error())
return
}
news, err := h.newsService.UpdateNews(uint(id), req, userID)
if err != nil {
if err.Error() == "access denied" {
h.logger.Warn("Access denied in UpdateNews",
zap.Uint("userID", userID),
zap.Uint("newsID", uint(id)),
)
utils.RespondWithError(w, http.StatusForbidden, "Access denied")
return
}
h.logger.Error("Failed to update news", zap.Error(err))
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to update news")
return
}
h.logger.Info("Successfully updated news",
zap.Uint("newsID", uint(id)),
zap.Uint("userID", userID),
)
h.logger.Debug("End UpdateNews Method")
utils.RespondWithJSON(w, http.StatusOK, news)
}
// DeleteNews удаляет новость
func (h *NewsHandler) DeleteNews(w http.ResponseWriter, r *http.Request) {
h.logger.Debug("Start DeleteNews Method")
userID, ok := r.Context().Value(middleware.UserIDKey).(uint)
h.logger.Debug("DeleteNews user context", zap.Uint("userID", userID), zap.Bool("ok", ok))
if !ok {
h.logger.Warn("Failed to get userID from context in DeleteNews")
utils.RespondWithError(w, http.StatusUnauthorized, "Unauthorized")
return
}
idStr := chi.URLParam(r, "id")
h.logger.Debug("DeleteNews parameters", zap.String("id", idStr))
id, err := strconv.ParseUint(idStr, 10, 32)
if err != nil {
h.logger.Warn("Invalid news ID in DeleteNews", zap.String("id", idStr), zap.Error(err))
utils.RespondWithError(w, http.StatusBadRequest, "Invalid news ID")
return
}
err = h.newsService.DeleteNews(uint(id), userID)
if err != nil {
if err.Error() == "access denied" {
h.logger.Warn("Access denied in DeleteNews",
zap.Uint("userID", userID),
zap.Uint("newsID", uint(id)),
)
utils.RespondWithError(w, http.StatusForbidden, "Access denied")
return
}
h.logger.Error("Failed to delete news", zap.Error(err))
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to delete news")
return
}
h.logger.Info("Successfully deleted news",
zap.Uint("newsID", uint(id)),
zap.Uint("userID", userID),
)
h.logger.Debug("End DeleteNews Method")
utils.RespondWithJSON(w, http.StatusOK, map[string]string{"message": "News deleted successfully"})
}
// CreateComment создает комментарий к новости
func (h *NewsHandler) CreateComment(w http.ResponseWriter, r *http.Request) {
h.logger.Debug("Start CreateComment Method")
userID, ok := r.Context().Value(middleware.UserIDKey).(uint)
h.logger.Debug("CreateComment user context", zap.Uint("userID", userID), zap.Bool("ok", ok))
if !ok {
h.logger.Warn("Failed to get userID from context in CreateComment")
utils.RespondWithError(w, http.StatusUnauthorized, "Unauthorized")
return
}
newsIDStr := chi.URLParam(r, "id")
h.logger.Debug("CreateComment parameters", zap.String("newsID", newsIDStr))
newsID, err := strconv.ParseUint(newsIDStr, 10, 32)
if err != nil {
h.logger.Warn("Invalid news ID in CreateComment", zap.String("newsID", newsIDStr), zap.Error(err))
utils.RespondWithError(w, http.StatusBadRequest, "Invalid news ID")
return
}
var req models.CreateCommentRequest
if err := utils.DecodeJSONBody(w, r, &req); err != nil {
h.logger.Warn("Invalid request body in CreateComment", zap.Error(err))
utils.RespondWithError(w, http.StatusBadRequest, "Invalid request body")
return
}
h.logger.Debug("CreateComment request data",
zap.String("content", req.Content),
)
if err := h.validator.Struct(req); err != nil {
h.logger.Warn("Validation failed in CreateComment", zap.Error(err))
utils.RespondWithError(w, http.StatusBadRequest, "Validation failed: "+err.Error())
return
}
comment, err := h.newsService.CreateComment(uint(newsID), req, userID)
if err != nil {
h.logger.Error("Failed to create comment", zap.Error(err))
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to create comment")
return
}
h.logger.Info("Successfully created comment",
zap.Uint("commentID", comment.ID),
zap.Uint("newsID", uint(newsID)),
zap.Uint("userID", userID),
)
h.logger.Debug("End CreateComment Method")
utils.RespondWithJSON(w, http.StatusCreated, comment)
}
// GetComments возвращает комментарии к новости
func (h *NewsHandler) GetComments(w http.ResponseWriter, r *http.Request) {
h.logger.Debug("Start GetComments Method")
newsIDStr := chi.URLParam(r, "id")
h.logger.Debug("GetComments parameters", zap.String("newsID", newsIDStr))
newsID, err := strconv.ParseUint(newsIDStr, 10, 32)
if err != nil {
h.logger.Warn("Invalid news ID in GetComments", zap.String("newsID", newsIDStr), zap.Error(err))
utils.RespondWithError(w, http.StatusBadRequest, "Invalid news ID")
return
}
comments, err := h.newsService.GetCommentsByNewsID(uint(newsID))
if err != nil {
h.logger.Error("Failed to get comments", zap.Error(err))
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get comments")
return
}
h.logger.Debug("Successfully retrieved comments",
zap.Uint("newsID", uint(newsID)),
zap.Int("count", len(comments)),
)
h.logger.Debug("End GetComments Method")
utils.RespondWithJSON(w, http.StatusOK, comments)
}
// DeleteComment удаляет комментарий
func (h *NewsHandler) DeleteComment(w http.ResponseWriter, r *http.Request) {
h.logger.Debug("Start DeleteComment Method")
userID, ok := r.Context().Value(middleware.UserIDKey).(uint)
h.logger.Debug("DeleteComment user context", zap.Uint("userID", userID), zap.Bool("ok", ok))
if !ok {
h.logger.Warn("Failed to get userID from context in DeleteComment")
utils.RespondWithError(w, http.StatusUnauthorized, "Unauthorized")
return
}
commentIDStr := chi.URLParam(r, "commentId")
h.logger.Debug("DeleteComment parameters", zap.String("commentID", commentIDStr))
commentID, err := strconv.ParseUint(commentIDStr, 10, 32)
if err != nil {
h.logger.Warn("Invalid comment ID in DeleteComment", zap.String("commentID", commentIDStr), zap.Error(err))
utils.RespondWithError(w, http.StatusBadRequest, "Invalid comment ID")
return
}
err = h.newsService.DeleteComment(uint(commentID), userID)
if err != nil {
if err.Error() == "access denied" {
h.logger.Warn("Access denied in DeleteComment",
zap.Uint("userID", userID),
zap.Uint("commentID", uint(commentID)),
)
utils.RespondWithError(w, http.StatusForbidden, "Access denied")
return
}
h.logger.Error("Failed to delete comment", zap.Error(err))
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to delete comment")
return
}
h.logger.Info("Successfully deleted comment",
zap.Uint("commentID", uint(commentID)),
zap.Uint("userID", userID),
)
h.logger.Debug("End DeleteComment Method")
utils.RespondWithJSON(w, http.StatusOK, map[string]string{"message": "Comment deleted successfully"})
}
// GetUserNews возвращает новости конкретного пользователя
func (h *NewsHandler) GetUserNews(w http.ResponseWriter, r *http.Request) {
h.logger.Debug("Start GetUserNews Method")
userID, ok := r.Context().Value(middleware.UserIDKey).(uint)
h.logger.Debug("GetUserNews user context", zap.Uint("userID", userID), zap.Bool("ok", ok))
if !ok {
h.logger.Warn("Failed to get userID from context in GetUserNews")
utils.RespondWithError(w, http.StatusUnauthorized, "Unauthorized")
return
}
limit, _ := strconv.Atoi(r.URL.Query().Get("limit"))
offset, _ := strconv.Atoi(r.URL.Query().Get("offset"))
h.logger.Debug("GetUserNews parameters",
zap.Int("limit", limit),
zap.Int("offset", offset),
)
if limit == 0 {
limit = 10
}
news, total, err := h.newsService.GetUserNews(userID, limit, offset)
if err != nil {
h.logger.Error("Failed to get user news", zap.Error(err))
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get user news")
return
}
h.logger.Debug("Successfully retrieved user news",
zap.Uint("userID", userID),
zap.Int("count", len(news)),
zap.Int("total", int(total)),
)
h.logger.Debug("End GetUserNews Method")
utils.RespondWithJSON(w, http.StatusOK, map[string]interface{}{
"news": news,
"total": total,
})
}
@@ -0,0 +1,506 @@
// handlers/personal_best_handler.go
package handlers
import (
"encoding/json"
"net/http"
"strconv"
"api_bb/internal/models"
"api_bb/internal/service"
"api_bb/pkg/logger"
"api_bb/pkg/middleware"
"api_bb/pkg/utils"
"go.uber.org/zap"
"github.com/go-chi/chi/v5"
)
type PersonalBestHandler struct {
logger logger.LoggerInterface
personalBestService service.PersonalBestService
}
func NewPersonalBestHandler(personalBestService service.PersonalBestService) *PersonalBestHandler {
return &PersonalBestHandler{
logger: logger.NewWrapper(logger.Get().With(zap.String("handler", "personal_best"))),
personalBestService: personalBestService,
}
}
// CreatePersonalBest создает новый личный рекорд
func (h *PersonalBestHandler) CreatePersonalBest(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling create personal best request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Получаем пользователя из контекста
user, ok := middleware.GetUserFromContext(r.Context())
if !ok {
h.logger.Warn("create personal best failed - authentication required")
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
var req models.PersonalBestCreateRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
h.logger.Error("failed to decode JSON payload", zap.Error(err))
utils.RespondWithError(w, http.StatusBadRequest, "Invalid request payload: "+err.Error())
return
}
// Валидация
if req.DistanceType == "" {
h.logger.Warn("create personal best failed - distance type required")
utils.RespondWithError(w, http.StatusBadRequest, "Distance type is required")
return
}
if req.Time == "" {
h.logger.Warn("create personal best failed - time required")
utils.RespondWithError(w, http.StatusBadRequest, "Time is required")
return
}
if req.Date.IsZero() {
h.logger.Warn("create personal best failed - date required")
utils.RespondWithError(w, http.StatusBadRequest, "Date is required")
return
}
personalBest, err := h.personalBestService.CreatePersonalBest(user.ID, req)
if err != nil {
h.logger.Error("failed to create personal best", zap.Error(err))
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to create personal best: "+err.Error())
return
}
h.logger.Info("personal best created successfully",
zap.Uint("user_id", user.ID),
zap.Uint("personal_best_id", personalBest.ID),
zap.String("distance_type", string(personalBest.DistanceType)),
)
utils.RespondWithJSON(w, http.StatusCreated, personalBest)
}
// GetPersonalBest возвращает личный рекорд по ID
func (h *PersonalBestHandler) GetPersonalBest(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling get personal best request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
idStr := chi.URLParam(r, "id")
id, err := strconv.ParseUint(idStr, 10, 32)
if err != nil {
h.logger.Warn("invalid personal best ID", zap.String("id", idStr))
utils.RespondWithError(w, http.StatusBadRequest, "Invalid personal best ID")
return
}
personalBest, err := h.personalBestService.GetPersonalBestByID(uint(id))
if err != nil {
h.logger.Error("failed to get personal best", zap.Error(err))
if err.Error() == "record not found" {
utils.RespondWithError(w, http.StatusNotFound, "Personal best not found")
} else {
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get personal best: "+err.Error())
}
return
}
h.logger.Info("personal best retrieved successfully",
zap.Uint("personal_best_id", personalBest.ID),
)
utils.RespondWithJSON(w, http.StatusOK, personalBest)
}
// GetUserPersonalBests возвращает все личные рекорды пользователя
func (h *PersonalBestHandler) GetUserPersonalBests(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling get user personal bests request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Получаем пользователя из контекста
user, ok := middleware.GetUserFromContext(r.Context())
if !ok {
h.logger.Warn("get personal bests failed - authentication required")
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
personalBests, err := h.personalBestService.GetUserPersonalBests(user.ID)
if err != nil {
h.logger.Error("failed to get personal bests", zap.Error(err))
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get personal bests: "+err.Error())
return
}
h.logger.Info("user personal bests retrieved successfully",
zap.Uint("user_id", user.ID),
zap.Int("count", len(personalBests)),
)
utils.RespondWithJSON(w, http.StatusOK, personalBests)
}
// UpdatePersonalBest обновляет личный рекорд
func (h *PersonalBestHandler) UpdatePersonalBest(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling update personal best request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Получаем пользователя из контекста
user, ok := middleware.GetUserFromContext(r.Context())
if !ok {
h.logger.Warn("update personal best failed - authentication required")
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
idStr := chi.URLParam(r, "id")
id, err := strconv.ParseUint(idStr, 10, 32)
if err != nil {
h.logger.Warn("invalid personal best ID", zap.String("id", idStr))
utils.RespondWithError(w, http.StatusBadRequest, "Invalid personal best ID")
return
}
var req models.PersonalBestUpdateRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
h.logger.Error("failed to decode JSON payload", zap.Error(err))
utils.RespondWithError(w, http.StatusBadRequest, "Invalid request payload: "+err.Error())
return
}
personalBest, err := h.personalBestService.UpdatePersonalBest(uint(id), user.ID, req)
if err != nil {
h.logger.Error("failed to update personal best", zap.Error(err))
if err.Error() == "record not found" {
utils.RespondWithError(w, http.StatusNotFound, "Personal best not found or access denied")
} else {
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to update personal best: "+err.Error())
}
return
}
h.logger.Info("personal best updated successfully",
zap.Uint("personal_best_id", personalBest.ID),
zap.Uint("user_id", user.ID),
)
utils.RespondWithJSON(w, http.StatusOK, personalBest)
}
// DeletePersonalBest удаляет личный рекорд
func (h *PersonalBestHandler) DeletePersonalBest(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling delete personal best request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Получаем пользователя из контекста
user, ok := middleware.GetUserFromContext(r.Context())
if !ok {
h.logger.Warn("delete personal best failed - authentication required")
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
idStr := chi.URLParam(r, "id")
id, err := strconv.ParseUint(idStr, 10, 32)
if err != nil {
h.logger.Warn("invalid personal best ID", zap.String("id", idStr))
utils.RespondWithError(w, http.StatusBadRequest, "Invalid personal best ID")
return
}
err = h.personalBestService.DeletePersonalBest(uint(id), user.ID)
if err != nil {
h.logger.Error("failed to delete personal best", zap.Error(err))
if err.Error() == "record not found" {
utils.RespondWithError(w, http.StatusNotFound, "Personal best not found or access denied")
} else {
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to delete personal best: "+err.Error())
}
return
}
h.logger.Info("personal best deleted successfully",
zap.Uint("personal_best_id", uint(id)),
zap.Uint("user_id", user.ID),
)
utils.RespondWithJSON(w, http.StatusOK, map[string]interface{}{
"message": "Personal best deleted successfully",
})
}
// GetPersonalBestsByDistance возвращает личные рекорды по дистанции
func (h *PersonalBestHandler) GetPersonalBestsByDistance(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling get personal bests by distance request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Получаем пользователя из контекста
user, ok := middleware.GetUserFromContext(r.Context())
if !ok {
h.logger.Warn("get personal bests by distance failed - authentication required")
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
distanceType := models.DistanceType(chi.URLParam(r, "distanceType"))
if distanceType == "" {
h.logger.Warn("distance type parameter is required")
utils.RespondWithError(w, http.StatusBadRequest, "Distance type parameter is required")
return
}
// Валидация типа дистанции
validDistances := map[models.DistanceType]bool{
models.Distance5K: true,
models.Distance10K: true,
models.DistanceHalf: true,
models.DistanceFull: true,
models.DistanceOther: true,
}
if !validDistances[distanceType] {
h.logger.Warn("invalid distance type", zap.String("distance_type", string(distanceType)))
utils.RespondWithError(w, http.StatusBadRequest, "Invalid distance type")
return
}
personalBests, err := h.personalBestService.GetPersonalBestsByDistance(user.ID, distanceType)
if err != nil {
h.logger.Error("failed to get personal bests by distance", zap.Error(err))
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get personal bests: "+err.Error())
return
}
h.logger.Info("personal bests by distance retrieved successfully",
zap.Uint("user_id", user.ID),
zap.String("distance_type", string(distanceType)),
zap.Int("count", len(personalBests)),
)
utils.RespondWithJSON(w, http.StatusOK, personalBests)
}
// GetBestByDistance возвращает лучший результат на дистанции
func (h *PersonalBestHandler) GetBestByDistance(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling get best by distance request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Получаем пользователя из контекста
user, ok := middleware.GetUserFromContext(r.Context())
if !ok {
h.logger.Warn("get best by distance failed - authentication required")
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
distanceType := models.DistanceType(chi.URLParam(r, "distanceType"))
if distanceType == "" {
h.logger.Warn("distance type parameter is required")
utils.RespondWithError(w, http.StatusBadRequest, "Distance type parameter is required")
return
}
best, err := h.personalBestService.GetBestByDistance(user.ID, distanceType)
if err != nil {
if err.Error() == "record not found" {
h.logger.Info("no personal best found for distance",
zap.Uint("user_id", user.ID),
zap.String("distance_type", string(distanceType)),
)
utils.RespondWithJSON(w, http.StatusOK, nil)
return
}
h.logger.Error("failed to get best by distance", zap.Error(err))
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get best result: "+err.Error())
return
}
h.logger.Info("best by distance retrieved successfully",
zap.Uint("user_id", user.ID),
zap.String("distance_type", string(distanceType)),
)
utils.RespondWithJSON(w, http.StatusOK, best)
}
// GetPersonalBestsSummary возвращает сводку лучших результатов
func (h *PersonalBestHandler) GetPersonalBestsSummary(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling get personal bests summary request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Получаем пользователя из контекста
user, ok := middleware.GetUserFromContext(r.Context())
if !ok {
h.logger.Warn("get personal bests summary failed - authentication required")
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
summary, err := h.personalBestService.GetPersonalBestsSummary(user.ID)
if err != nil {
h.logger.Error("failed to get personal bests summary", zap.Error(err))
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get personal bests summary: "+err.Error())
return
}
h.logger.Info("personal bests summary retrieved successfully",
zap.Uint("user_id", user.ID),
)
utils.RespondWithJSON(w, http.StatusOK, summary)
}
// VerifyPersonalBest подтверждает личный рекорд
func (h *PersonalBestHandler) VerifyPersonalBest(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling verify personal best request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Получаем пользователя из контекста
user, ok := middleware.GetUserFromContext(r.Context())
if !ok {
h.logger.Warn("verify personal best failed - authentication required")
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
idStr := chi.URLParam(r, "id")
id, err := strconv.ParseUint(idStr, 10, 32)
if err != nil {
h.logger.Warn("invalid personal best ID", zap.String("id", idStr))
utils.RespondWithError(w, http.StatusBadRequest, "Invalid personal best ID")
return
}
err = h.personalBestService.VerifyPersonalBest(uint(id), user.ID)
if err != nil {
h.logger.Error("failed to verify personal best", zap.Error(err))
if err.Error() == "record not found" {
utils.RespondWithError(w, http.StatusNotFound, "Personal best not found or access denied")
} else {
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to verify personal best: "+err.Error())
}
return
}
h.logger.Info("personal best verified successfully",
zap.Uint("personal_best_id", uint(id)),
zap.Uint("user_id", user.ID),
)
utils.RespondWithJSON(w, http.StatusOK, map[string]interface{}{
"message": "Personal best verified successfully",
})
}
// GetRecentPersonalBests возвращает последние личные рекорды
func (h *PersonalBestHandler) GetRecentPersonalBests(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling get recent personal bests request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Получаем пользователя из контекста
user, ok := middleware.GetUserFromContext(r.Context())
if !ok {
h.logger.Warn("get recent personal bests failed - authentication required")
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
limit := 10 // default limit
limitStr := r.URL.Query().Get("limit")
if limitStr != "" {
if l, err := strconv.Atoi(limitStr); err == nil && l > 0 {
limit = l
}
}
personalBests, err := h.personalBestService.GetRecentPersonalBests(user.ID, limit)
if err != nil {
h.logger.Error("failed to get recent personal bests", zap.Error(err))
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get recent personal bests: "+err.Error())
return
}
h.logger.Info("recent personal bests retrieved successfully",
zap.Uint("user_id", user.ID),
zap.Int("limit", limit),
zap.Int("count", len(personalBests)),
)
utils.RespondWithJSON(w, http.StatusOK, personalBests)
}
// CalculatePace вычисляет темп
func (h *PersonalBestHandler) CalculatePace(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling calculate pace request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
var req struct {
Time string `json:"time"`
DistanceType models.DistanceType `json:"distance_type"`
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
h.logger.Error("failed to decode JSON payload", zap.Error(err))
utils.RespondWithError(w, http.StatusBadRequest, "Invalid request payload: "+err.Error())
return
}
if req.Time == "" || req.DistanceType == "" {
h.logger.Warn("time and distance type are required")
utils.RespondWithError(w, http.StatusBadRequest, "Time and distance type are required")
return
}
pace, err := h.personalBestService.CalculatePace(req.Time, req.DistanceType)
if err != nil {
h.logger.Error("failed to calculate pace", zap.Error(err))
utils.RespondWithError(w, http.StatusBadRequest, "Failed to calculate pace: "+err.Error())
return
}
h.logger.Info("pace calculated successfully",
zap.String("time", req.Time),
zap.String("distance_type", string(req.DistanceType)),
zap.String("pace", pace),
)
utils.RespondWithJSON(w, http.StatusOK, map[string]interface{}{
"time": req.Time,
"distance_type": req.DistanceType,
"pace": pace,
})
}
@@ -0,0 +1,269 @@
// handlers/review_handler.go
package handlers
import (
"api_bb/internal/models"
"api_bb/internal/service"
"api_bb/pkg/logger"
"api_bb/pkg/middleware"
"api_bb/pkg/utils"
"net/http"
"strconv"
"github.com/go-chi/chi/v5"
"go.uber.org/zap"
)
// ReviewHandler обрабатывает HTTP-запросы, связанные с отзывами
type ReviewHandler struct {
reviewService service.ReviewService // Сервис для работы с отзывами
logger logger.LoggerInterface // Логгер для записи событий
}
// NewReviewHandler создает новый экземпляр ReviewHandler
func NewReviewHandler(reviewService service.ReviewService, logger logger.LoggerInterface) *ReviewHandler {
return &ReviewHandler{
reviewService: reviewService,
logger: logger,
}
}
// GetReviews возвращает список отзывов с пагинацией и фильтрацией
func (h *ReviewHandler) GetReviews(w http.ResponseWriter, r *http.Request) {
page, _ := strconv.Atoi(r.URL.Query().Get("page"))
limit, _ := strconv.Atoi(r.URL.Query().Get("limit"))
sortBy := r.URL.Query().Get("sort")
filter := r.URL.Query().Get("filter")
if page < 1 {
page = 1
}
if limit < 1 {
limit = 6
}
reviews, totalPages, err := h.reviewService.GetAllReviews(page, limit, sortBy, filter)
if err != nil {
h.logger.Error("Failed to get reviews", zap.Error(err))
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get reviews")
return
}
response := map[string]interface{}{
"reviews": reviews,
"current_page": page,
"total_pages": totalPages,
"total_items": len(reviews),
}
utils.RespondWithJSON(w, http.StatusOK, response)
}
// GetReviewsStats возвращает статистику отзывов
func (h *ReviewHandler) GetReviewsStats(w http.ResponseWriter, r *http.Request) {
stats, err := h.reviewService.GetReviewsStats()
if err != nil {
h.logger.Error("Failed to get reviews stats", zap.Error(err))
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get reviews statistics")
return
}
utils.RespondWithJSON(w, http.StatusOK, stats)
}
// GetMyReviews возвращает отзывы текущего аутентифицированного пользователя
func (h *ReviewHandler) GetMyReviews(w http.ResponseWriter, r *http.Request) {
// Получаем ID пользователя из контекста (добавляется middleware аутентификации)
userID, ok := r.Context().Value("middleware.UserIDKey").(uint)
if !ok {
h.logger.Warn("Failed to get userID from context in GetMyReviews",
zap.String("path", r.URL.Path),
zap.String("method", r.Method),
zap.String("remote_addr", r.RemoteAddr),
)
utils.RespondWithError(w, http.StatusUnauthorized, "User not authenticated")
return
}
// Получаем отзывы пользователя из сервиса
reviews, err := h.reviewService.GetUserReviews(userID)
if err != nil {
h.logger.With(zap.Int("userID", int(userID))).Error("Failed to get user reviews", zap.Error(err))
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get your reviews")
return
}
utils.RespondWithJSON(w, http.StatusOK, reviews)
}
// CreateReview создает новый отзыв от имени текущего пользователя
func (h *ReviewHandler) CreateReview(w http.ResponseWriter, r *http.Request) {
// Получаем ID пользователя из контекста
userID, ok := r.Context().Value(middleware.UserIDKey).(uint)
if !ok {
h.logger.Warn("Failed to get userID from context in CreateReview",
zap.String("path", r.URL.Path),
zap.String("method", r.Method),
zap.String("remote_addr", r.RemoteAddr),
zap.Uint("userID", userID),
)
utils.RespondWithError(w, http.StatusUnauthorized, "User not authenticated")
return
}
h.logger.Debug("Successfully extracted userID from context",
zap.Uint("userID", userID),
zap.String("path", r.URL.Path),
zap.String("method", r.Method),
)
// Декодируем тело запроса
var req models.CreateReviewRequest
if err := utils.DecodeJSONBody(w, r, &req); err != nil {
h.logger.Error("Failed to decode review request",
zap.Error(err),
zap.Uint("userID", userID),
)
utils.RespondWithError(w, http.StatusBadRequest, "Invalid request body")
return
}
// Создаем отзыв через сервис
review, err := h.reviewService.CreateReview(&req, userID)
if err != nil {
h.logger.With(zap.Int("userID", int(userID))).Error("Failed to create review", zap.Error(err))
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to create review")
return
}
h.logger.Info("Review created successfully",
zap.Uint("userID", userID),
zap.Any("review_id", review.ID),
)
utils.RespondWithJSON(w, http.StatusCreated, review)
}
// GetReviewByID возвращает отзыв по его идентификатору
func (h *ReviewHandler) GetReviewByID(w http.ResponseWriter, r *http.Request) {
// Получаем ID отзыва из параметров URL
idStr := chi.URLParam(r, "id")
id, err := strconv.ParseUint(idStr, 10, 32)
if err != nil {
utils.RespondWithError(w, http.StatusBadRequest, "Invalid review ID")
return
}
// Получаем отзыв из сервиса
review, err := h.reviewService.GetReviewByID(uint(id))
if err != nil {
h.logger.With(zap.Int("id", int(id))).Error("Failed to get review", zap.Error(err))
utils.RespondWithError(w, http.StatusNotFound, "Review not found")
return
}
utils.RespondWithJSON(w, http.StatusOK, review)
}
// UpdateReview обновляет существующий отзыв
func (h *ReviewHandler) UpdateReview(w http.ResponseWriter, r *http.Request) {
// Получаем ID пользователя из контекста
userID, ok := r.Context().Value(middleware.UserIDKey).(uint)
if !ok {
h.logger.Warn("Failed to get userID from context in UpdateReview",
zap.String("path", r.URL.Path),
zap.String("method", r.Method),
zap.String("remote_addr", r.RemoteAddr),
zap.Uint("userID", userID),
)
utils.RespondWithError(w, http.StatusUnauthorized, "User not authenticated")
return
}
// Получаем флаг администратора из контекста
isAdmin, _ := r.Context().Value("IsAdmin").(bool)
// Получаем ID отзыва из параметров URL
idStr := chi.URLParam(r, "id")
id, err := strconv.ParseUint(idStr, 10, 32)
if err != nil {
utils.RespondWithError(w, http.StatusBadRequest, "Invalid review ID")
return
}
// Декодируем тело запроса
var req models.UpdateReviewRequest
if err := utils.DecodeJSONBody(w, r, &req); err != nil {
h.logger.Error("Failed to decode update review request",
zap.Error(err),
zap.Uint("userID", userID),
zap.Uint("review_id", uint(id)),
)
utils.RespondWithError(w, http.StatusBadRequest, "Invalid request body")
return
}
// Обновляем отзыв через сервис
review, err := h.reviewService.UpdateReview(uint(id), &req, userID, isAdmin)
if err != nil {
h.logger.With(zap.Int("id", int(id))).With(zap.Int("userID", int(userID))).Error("Failed to update review", zap.Error(err))
if err.Error() == "unauthorized" {
utils.RespondWithError(w, http.StatusForbidden, "You can only update your own reviews")
return
}
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to update review")
return
}
h.logger.Info("Review updated successfully",
zap.Uint("userID", userID),
zap.Uint("review_id", uint(id)),
)
utils.RespondWithJSON(w, http.StatusOK, review)
}
// DeleteReview удаляет отзыв
func (h *ReviewHandler) DeleteReview(w http.ResponseWriter, r *http.Request) {
// Получаем ID пользователя из контекста
userID, ok := r.Context().Value(middleware.UserIDKey).(uint)
if !ok {
h.logger.Warn("Failed to get userID from context in DeleteReview",
zap.String("path", r.URL.Path),
zap.String("method", r.Method),
zap.String("remote_addr", r.RemoteAddr),
)
utils.RespondWithError(w, http.StatusUnauthorized, "User not authenticated")
return
}
// Получаем флаг администратора из контекста
isAdmin, _ := r.Context().Value("IsAdmin").(bool)
// Получаем ID отзыва из параметров URL
idStr := chi.URLParam(r, "id")
id, err := strconv.ParseUint(idStr, 10, 32)
if err != nil {
utils.RespondWithError(w, http.StatusBadRequest, "Invalid review ID")
return
}
// Удаляем отзыв через сервис
err = h.reviewService.DeleteReview(uint(id), userID, isAdmin)
if err != nil {
h.logger.With(zap.Int("id", int(id))).With(zap.Int("userID", int(userID))).Error("Failed to delete review", zap.Error(err))
if err.Error() == "unauthorized" {
utils.RespondWithError(w, http.StatusForbidden, "You can only delete your own reviews")
return
}
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to delete review")
return
}
h.logger.Info("Review deleted successfully",
zap.Uint("userID", userID),
zap.Uint("review_id", uint(id)),
)
utils.RespondWithJSON(w, http.StatusOK, map[string]string{"message": "Review deleted successfully"})
}
@@ -0,0 +1,557 @@
// handlers/training_plan_handler.go
package handlers
import (
"encoding/json"
"net/http"
"strconv"
"time"
"api_bb/internal/models"
"api_bb/internal/service"
"api_bb/pkg/logger"
"api_bb/pkg/middleware"
"api_bb/pkg/utils"
"go.uber.org/zap"
)
type TrainingPlanHandler struct {
logger logger.LoggerInterface
trainingPlanService service.TrainingPlanService
}
func NewTrainingPlanHandler(trainingPlanService service.TrainingPlanService) *TrainingPlanHandler {
return &TrainingPlanHandler{
logger: logger.NewWrapper(logger.Get().With(zap.String("handler", "training_plan"))),
trainingPlanService: trainingPlanService,
}
}
// TrainingPlanResponse - DTO для ответа с планом тренировок
type TrainingPlanResponse struct {
ID uint `json:"id"`
UserID uint `json:"user_id"`
Title string `json:"title"`
Description string `json:"description"`
Weeks int `json:"weeks"`
WorkoutsPerWeek int `json:"workouts_per_week"`
TargetDistance string `json:"target_distance"`
TargetDate time.Time `json:"target_date"`
CurrentWeek int `json:"current_week"`
Completed bool `json:"completed"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
Workouts []TrainingWorkoutResponse `json:"workouts,omitempty"`
}
// TrainingWorkoutResponse - DTO для ответа с тренировкой плана
type TrainingWorkoutResponse struct {
ID uint `json:"id"`
PlanID uint `json:"plan_id"`
Week int `json:"week"`
Day int `json:"day"`
Type models.WorkoutType `json:"type"`
Description string `json:"description"`
Distance float64 `json:"distance_km"`
Duration int `json:"duration_min"`
Completed bool `json:"completed"`
CompletedAt *time.Time `json:"completed_at"`
CreatedAt time.Time `json:"created_at"`
}
// CreateTrainingPlan создает новый план тренировок
func (h *TrainingPlanHandler) CreateTrainingPlan(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling create training plan request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Получаем пользователя из контекста
user, ok := middleware.GetUserFromContext(r.Context())
if !ok {
h.logger.Warn("create training plan failed - authentication required")
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
var req models.TrainingPlanCreateRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
h.logger.Error("failed to decode JSON payload", zap.Error(err))
utils.RespondWithError(w, http.StatusBadRequest, "Invalid request payload: "+err.Error())
return
}
h.logger.Debug("creating training plan",
zap.Uint("user_id", user.ID),
zap.String("title", req.Title),
zap.Int("weeks", req.Weeks),
zap.Int("workouts_per_week", req.WorkoutsPerWeek),
)
// Создаем план тренировок через сервис
plan, err := h.trainingPlanService.CreateTrainingPlan(user.ID, &req)
if err != nil {
h.logger.Error("failed to create training plan in service",
zap.Uint("user_id", user.ID),
zap.Error(err),
)
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to create training plan: "+err.Error())
return
}
h.logger.Info("training plan created successfully",
zap.Uint("user_id", user.ID),
zap.Uint("plan_id", plan.ID),
)
utils.RespondWithJSON(w, http.StatusCreated, map[string]interface{}{
"message": "Training plan created successfully",
"plan": toTrainingPlanResponse(plan),
})
}
// GetTrainingPlans возвращает все планы тренировок пользователя
func (h *TrainingPlanHandler) GetTrainingPlans(w http.ResponseWriter, r *http.Request) {
h.logger.Debug("handling get training plans request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Получаем пользователя из контекста
user, ok := middleware.GetUserFromContext(r.Context())
if !ok {
h.logger.Warn("get training plans failed - authentication required")
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
h.logger.Debug("getting training plans for user", zap.Uint("user_id", user.ID))
// Получаем планы тренировок через сервис
plans, err := h.trainingPlanService.GetTrainingPlansByUserID(user.ID)
if err != nil {
h.logger.Error("failed to get training plans from service",
zap.Uint("user_id", user.ID),
zap.Error(err),
)
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get training plans: "+err.Error())
return
}
// Преобразуем в response формат
var planResponses []TrainingPlanResponse
for _, plan := range plans {
planResponses = append(planResponses, toTrainingPlanResponse(&plan))
}
h.logger.Debug("training plans retrieved successfully",
zap.Uint("user_id", user.ID),
zap.Int("plans_count", len(planResponses)),
)
utils.RespondWithJSON(w, http.StatusOK, planResponses)
}
// GetTrainingPlanByID возвращает план тренировок по ID
func (h *TrainingPlanHandler) GetTrainingPlanByID(w http.ResponseWriter, r *http.Request) {
h.logger.Debug("handling get training plan by ID request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Получаем пользователя из контекста
user, ok := middleware.GetUserFromContext(r.Context())
if !ok {
h.logger.Warn("get training plan failed - authentication required")
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
// Извлекаем ID плана из URL параметров
planIDStr := r.URL.Query().Get("id")
if planIDStr == "" {
h.logger.Warn("get training plan failed - plan ID required")
utils.RespondWithError(w, http.StatusBadRequest, "Plan ID is required")
return
}
planID, err := strconv.ParseUint(planIDStr, 10, 32)
if err != nil {
h.logger.Warn("get training plan failed - invalid plan ID",
zap.String("plan_id", planIDStr),
zap.Error(err),
)
utils.RespondWithError(w, http.StatusBadRequest, "Invalid plan ID")
return
}
h.logger.Debug("getting training plan by ID",
zap.Uint("user_id", user.ID),
zap.Uint("plan_id", uint(planID)),
)
// Получаем план тренировок через сервис
plan, err := h.trainingPlanService.GetTrainingPlanByID(user.ID, uint(planID))
if err != nil {
h.logger.Error("failed to get training plan from service",
zap.Uint("user_id", user.ID),
zap.Uint("plan_id", uint(planID)),
zap.Error(err),
)
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get training plan: "+err.Error())
return
}
h.logger.Debug("training plan retrieved successfully",
zap.Uint("user_id", user.ID),
zap.Uint("plan_id", uint(planID)),
)
utils.RespondWithJSON(w, http.StatusOK, toTrainingPlanResponse(plan))
}
// UpdateTrainingPlan обновляет план тренировок
func (h *TrainingPlanHandler) UpdateTrainingPlan(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling update training plan request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Получаем пользователя из контекста
user, ok := middleware.GetUserFromContext(r.Context())
if !ok {
h.logger.Warn("update training plan failed - authentication required")
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
// Извлекаем ID плана из URL параметров
planIDStr := r.URL.Query().Get("id")
if planIDStr == "" {
h.logger.Warn("update training plan failed - plan ID required")
utils.RespondWithError(w, http.StatusBadRequest, "Plan ID is required")
return
}
planID, err := strconv.ParseUint(planIDStr, 10, 32)
if err != nil {
h.logger.Warn("update training plan failed - invalid plan ID",
zap.String("plan_id", planIDStr),
zap.Error(err),
)
utils.RespondWithError(w, http.StatusBadRequest, "Invalid plan ID")
return
}
var req models.TrainingPlanUpdateRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
h.logger.Error("failed to decode JSON payload", zap.Error(err))
utils.RespondWithError(w, http.StatusBadRequest, "Invalid request payload: "+err.Error())
return
}
h.logger.Info("updating training plan",
zap.Uint("user_id", user.ID),
zap.Uint("plan_id", uint(planID)),
zap.String("title", req.Title),
)
// Обновляем план тренировок через сервис
plan, err := h.trainingPlanService.UpdateTrainingPlan(user.ID, uint(planID), &req)
if err != nil {
h.logger.Error("failed to update training plan in service",
zap.Uint("user_id", user.ID),
zap.Uint("plan_id", uint(planID)),
zap.Error(err),
)
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to update training plan: "+err.Error())
return
}
h.logger.Info("training plan updated successfully",
zap.Uint("user_id", user.ID),
zap.Uint("plan_id", uint(planID)),
)
utils.RespondWithJSON(w, http.StatusOK, map[string]interface{}{
"message": "Training plan updated successfully",
"plan": toTrainingPlanResponse(plan),
})
}
// DeleteTrainingPlan удаляет план тренировок
func (h *TrainingPlanHandler) DeleteTrainingPlan(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling delete training plan request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Получаем пользователя из контекста
user, ok := middleware.GetUserFromContext(r.Context())
if !ok {
h.logger.Warn("delete training plan failed - authentication required")
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
// Извлекаем ID плана из URL параметров
planIDStr := r.URL.Query().Get("id")
if planIDStr == "" {
h.logger.Warn("delete training plan failed - plan ID required")
utils.RespondWithError(w, http.StatusBadRequest, "Plan ID is required")
return
}
planID, err := strconv.ParseUint(planIDStr, 10, 32)
if err != nil {
h.logger.Warn("delete training plan failed - invalid plan ID",
zap.String("plan_id", planIDStr),
zap.Error(err),
)
utils.RespondWithError(w, http.StatusBadRequest, "Invalid plan ID")
return
}
h.logger.Info("deleting training plan",
zap.Uint("user_id", user.ID),
zap.Uint("plan_id", uint(planID)),
)
// Удаляем план тренировок через сервис
if err := h.trainingPlanService.DeleteTrainingPlan(user.ID, uint(planID)); err != nil {
h.logger.Error("failed to delete training plan in service",
zap.Uint("user_id", user.ID),
zap.Uint("plan_id", uint(planID)),
zap.Error(err),
)
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to delete training plan: "+err.Error())
return
}
h.logger.Info("training plan deleted successfully",
zap.Uint("user_id", user.ID),
zap.Uint("plan_id", uint(planID)),
)
utils.RespondWithJSON(w, http.StatusOK, map[string]interface{}{
"message": "Training plan deleted successfully",
})
}
// GetActiveTrainingPlan возвращает активный план тренировок пользователя
func (h *TrainingPlanHandler) GetActiveTrainingPlan(w http.ResponseWriter, r *http.Request) {
h.logger.Debug("handling get active training plan request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Получаем пользователя из контекста
user, ok := middleware.GetUserFromContext(r.Context())
if !ok {
h.logger.Warn("get active training plan failed - authentication required")
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
h.logger.Debug("getting active training plan for user", zap.Uint("user_id", user.ID))
// Получаем активный план тренировок через сервис
plan, err := h.trainingPlanService.GetActiveTrainingPlan(user.ID)
if err != nil {
h.logger.Error("failed to get active training plan from service",
zap.Uint("user_id", user.ID),
zap.Error(err),
)
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get active training plan: "+err.Error())
return
}
h.logger.Debug("active training plan retrieved successfully",
zap.Uint("user_id", user.ID),
zap.Uint("plan_id", plan.ID),
)
utils.RespondWithJSON(w, http.StatusOK, toTrainingPlanResponse(plan))
}
// MarkTrainingPlanAsCompleted помечает план тренировок как завершенный
func (h *TrainingPlanHandler) MarkTrainingPlanAsCompleted(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling mark training plan as completed request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Получаем пользователя из контекста
user, ok := middleware.GetUserFromContext(r.Context())
if !ok {
h.logger.Warn("mark training plan as completed failed - authentication required")
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
// Извлекаем ID плана из URL параметров
planIDStr := r.URL.Query().Get("id")
if planIDStr == "" {
h.logger.Warn("mark training plan as completed failed - plan ID required")
utils.RespondWithError(w, http.StatusBadRequest, "Plan ID is required")
return
}
planID, err := strconv.ParseUint(planIDStr, 10, 32)
if err != nil {
h.logger.Warn("mark training plan as completed failed - invalid plan ID",
zap.String("plan_id", planIDStr),
zap.Error(err),
)
utils.RespondWithError(w, http.StatusBadRequest, "Invalid plan ID")
return
}
h.logger.Info("marking training plan as completed",
zap.Uint("user_id", user.ID),
zap.Uint("plan_id", uint(planID)),
)
// Помечаем план как завершенный через сервис
if err := h.trainingPlanService.MarkTrainingPlanAsCompleted(user.ID, uint(planID)); err != nil {
h.logger.Error("failed to mark training plan as completed in service",
zap.Uint("user_id", user.ID),
zap.Uint("plan_id", uint(planID)),
zap.Error(err),
)
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to mark training plan as completed: "+err.Error())
return
}
h.logger.Info("training plan marked as completed successfully",
zap.Uint("user_id", user.ID),
zap.Uint("plan_id", uint(planID)),
)
utils.RespondWithJSON(w, http.StatusOK, map[string]interface{}{
"message": "Training plan marked as completed successfully",
})
}
// UpdateCurrentWeek обновляет текущую неделю плана тренировок
func (h *TrainingPlanHandler) UpdateCurrentWeek(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling update current week request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Получаем пользователя из контекста
user, ok := middleware.GetUserFromContext(r.Context())
if !ok {
h.logger.Warn("update current week failed - authentication required")
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
// Извлекаем ID плана из URL параметров
planIDStr := r.URL.Query().Get("id")
if planIDStr == "" {
h.logger.Warn("update current week failed - plan ID required")
utils.RespondWithError(w, http.StatusBadRequest, "Plan ID is required")
return
}
planID, err := strconv.ParseUint(planIDStr, 10, 32)
if err != nil {
h.logger.Warn("update current week failed - invalid plan ID",
zap.String("plan_id", planIDStr),
zap.Error(err),
)
utils.RespondWithError(w, http.StatusBadRequest, "Invalid plan ID")
return
}
var req struct {
CurrentWeek int `json:"current_week" validate:"required,min=1,max=52"`
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
h.logger.Error("failed to decode JSON payload", zap.Error(err))
utils.RespondWithError(w, http.StatusBadRequest, "Invalid request payload: "+err.Error())
return
}
h.logger.Info("updating current week for training plan",
zap.Uint("user_id", user.ID),
zap.Uint("plan_id", uint(planID)),
zap.Int("current_week", req.CurrentWeek),
)
// Обновляем текущую неделю через сервис
if err := h.trainingPlanService.UpdateCurrentWeek(user.ID, uint(planID), req.CurrentWeek); err != nil {
h.logger.Error("failed to update current week in service",
zap.Uint("user_id", user.ID),
zap.Uint("plan_id", uint(planID)),
zap.Error(err),
)
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to update current week: "+err.Error())
return
}
h.logger.Info("current week updated successfully",
zap.Uint("user_id", user.ID),
zap.Uint("plan_id", uint(planID)),
zap.Int("current_week", req.CurrentWeek),
)
utils.RespondWithJSON(w, http.StatusOK, map[string]interface{}{
"message": "Current week updated successfully",
})
}
// Вспомогательные функции для преобразования моделей в DTO
func toTrainingPlanResponse(plan *models.TrainingPlan) TrainingPlanResponse {
response := TrainingPlanResponse{
ID: plan.ID,
UserID: plan.UserID,
Title: plan.Title,
Description: plan.Description,
Weeks: plan.Weeks,
WorkoutsPerWeek: plan.WorkoutsPerWeek,
TargetDistance: plan.TargetDistance,
TargetDate: plan.TargetDate,
CurrentWeek: plan.CurrentWeek,
Completed: plan.Completed,
CreatedAt: plan.CreatedAt,
UpdatedAt: plan.UpdatedAt,
}
// Преобразуем тренировки, если они загружены
if plan.Workouts != nil {
for _, workout := range plan.Workouts {
response.Workouts = append(response.Workouts, toTrainingWorkoutResponse(&workout))
}
}
return response
}
func toTrainingWorkoutResponse(workout *models.TrainingWorkout) TrainingWorkoutResponse {
return TrainingWorkoutResponse{
ID: workout.ID,
PlanID: workout.PlanID,
Week: workout.Week,
Day: workout.Day,
Type: workout.Type,
Description: workout.Description,
Distance: workout.Distance,
Duration: workout.Duration,
Completed: workout.Completed,
CompletedAt: workout.CompletedAt,
CreatedAt: workout.CreatedAt,
}
}
+204
View File
@@ -0,0 +1,204 @@
// handlers/user.go
package handlers
import (
"bytes"
"encoding/json"
"io"
"net/http"
"time"
"api_bb/internal/models"
"api_bb/internal/service"
"api_bb/pkg/logger"
"api_bb/pkg/middleware"
"api_bb/pkg/utils"
"go.uber.org/zap"
)
type UserHandler struct {
logger logger.LoggerInterface
userService service.UserService
}
func NewUserHandler(userService service.UserService) *UserHandler {
return &UserHandler{
logger: logger.NewWrapper(logger.Get().With(zap.String("handler", "user"))),
userService: userService,
}
}
type UserResponse struct {
ID uint `json:"id"`
Email string `json:"email"`
FirstName string `json:"firstName"`
LastName string `json:"lastName"`
Avatar string `json:"avatar"`
Phone string `json:"phone"`
Experience string `json:"experience"`
Goals string `json:"goals"`
Newsletter bool `json:"newsletter"`
Role string `json:"role"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
// GetUsers возвращает список всех пользователей
func (h *UserHandler) GetUsers(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling get users request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Получаем пользователя из контекста для проверки аутентификации
_, ok := middleware.GetUserFromContext(r.Context())
if !ok {
h.logger.Warn("get users failed - authentication required")
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
// Получаем список пользователей из сервиса
users, err := h.userService.GetAllUsers()
if err != nil {
h.logger.Error("failed to get users from service", zap.Error(err))
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get users: "+err.Error())
return
}
// Преобразуем в response формат
var userResponses []UserResponse
for _, user := range users {
userResponses = append(userResponses, toUserResponse(&user))
}
h.logger.Info("users list retrieved successfully",
zap.Int("users_count", len(userResponses)),
)
utils.RespondWithJSON(w, http.StatusOK, userResponses)
}
func (h *UserHandler) GetProfile(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling get profile request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
user, ok := middleware.GetUserFromContext(r.Context())
if !ok {
h.logger.Warn("get profile failed - authentication required")
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
h.logger.Info("profile retrieved successfully",
zap.Uint("user_id", user.ID),
zap.String("email", user.Email),
zap.String("avatar", user.Avatar),
)
utils.RespondWithJSON(w, http.StatusOK, toUserResponse(user))
}
type UpdateProfileRequest struct {
FirstName string `json:"firstName"`
LastName string `json:"lastName"`
Phone string `json:"phone"`
Experience string `json:"experience"`
Goals string `json:"goals"`
Newsletter bool `json:"newsletter"`
}
func (h *UserHandler) UpdateProfile(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling update profile request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Логируем тело запроса для отладки
bodyBytes, err := io.ReadAll(r.Body)
if err != nil {
h.logger.Error("failed to read request body", zap.Error(err))
utils.RespondWithError(w, http.StatusBadRequest, "Failed to read request body: "+err.Error())
return
}
// Восстанавливаем тело для дальнейшего использования
r.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
h.logger.Debug("raw request body", zap.String("body", string(bodyBytes)))
// Получаем пользователя из контекста
currentUser, ok := middleware.GetUserFromContext(r.Context())
if !ok {
h.logger.Warn("update profile failed - authentication required")
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
var req UpdateProfileRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
h.logger.Error("failed to decode JSON payload", zap.Error(err))
utils.RespondWithError(w, http.StatusBadRequest, "Invalid request payload: "+err.Error())
return
}
// Валидация обязательных полей
if req.FirstName == "" {
h.logger.Warn("update profile failed - first name required")
utils.RespondWithError(w, http.StatusBadRequest, "First name is required")
return
}
if req.LastName == "" {
h.logger.Warn("update profile failed - last name required")
utils.RespondWithError(w, http.StatusBadRequest, "Last name is required")
return
}
h.logger.Info("updating user profile",
zap.Uint("user_id", currentUser.ID),
zap.String("first_name", req.FirstName),
zap.String("last_name", req.LastName),
zap.String("experience", req.Experience),
zap.String("goals", req.Goals),
zap.Bool("newsletter", req.Newsletter),
)
// Обновляем данные пользователя
updatedUser := &models.User{
ID: currentUser.ID,
FirstName: req.FirstName,
LastName: req.LastName,
Phone: req.Phone,
Experience: req.Experience,
Goals: req.Goals,
Newsletter: req.Newsletter,
UpdatedAt: time.Now(),
}
// Сохраняем обновленные данные
if err := h.userService.UpdateProfile(updatedUser); err != nil {
h.logger.Error("failed to update profile in service",
zap.Uint("user_id", currentUser.ID),
zap.Error(err),
)
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to update profile: "+err.Error())
return
}
h.logger.Info("profile updated successfully",
zap.Uint("user_id", currentUser.ID),
)
utils.RespondWithJSON(w, http.StatusOK, map[string]interface{}{
"message": "Profile updated successfully",
"user": toUserResponse(updatedUser),
})
}
@@ -0,0 +1,618 @@
// handlers/user_achievement_handler.go
package handlers
import (
"net/http"
"strconv"
"api_bb/internal/models"
"api_bb/internal/service"
"api_bb/pkg/logger"
"api_bb/pkg/middleware"
"api_bb/pkg/utils"
"github.com/go-chi/chi/v5"
"go.uber.org/zap"
)
type UserAchievementHandler struct {
logger logger.LoggerInterface
achievementService service.AchievementService
}
func NewUserAchievementHandler(achievementService service.AchievementService) *UserAchievementHandler {
return &UserAchievementHandler{
logger: logger.NewWrapper(logger.Get().With(zap.String("handler", "user_achievement"))),
achievementService: achievementService,
}
}
// GetAchievementsByType возвращает достижения по типу
func (h *UserAchievementHandler) GetAchievementsByType(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling get achievements by type request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Получаем пользователя из контекста
user, ok := middleware.GetUserFromContext(r.Context())
if !ok {
h.logger.Warn("get achievements by type failed - authentication required")
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
// Получаем тип из URL параметров
achievementType := chi.URLParam(r, "type")
if achievementType == "" {
utils.RespondWithError(w, http.StatusBadRequest, "Achievement type is required")
return
}
// Валидируем тип достижения
validType := models.AchievementType(achievementType)
switch validType {
case models.AchievementTypeDistance, models.AchievementTypeSpeed,
models.AchievementTypeConsistency, models.AchievementTypeEvent,
models.AchievementTypeSpecial:
// valid type
default:
utils.RespondWithError(w, http.StatusBadRequest, "Invalid achievement type")
return
}
achievements, err := h.achievementService.GetAchievementsByType(user.ID, validType)
if err != nil {
h.logger.Error("failed to get achievements by type",
zap.Uint("user_id", user.ID),
zap.String("type", achievementType),
zap.Error(err),
)
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get achievements: "+err.Error())
return
}
h.logger.Info("achievements by type retrieved successfully",
zap.Uint("user_id", user.ID),
zap.String("type", achievementType),
zap.Int("achievements_count", len(achievements)),
)
utils.RespondWithJSON(w, http.StatusOK, achievements)
}
// GetAchievementByID возвращает достижение по ID
func (h *UserAchievementHandler) GetAchievementByID(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling get achievement by ID request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Получаем пользователя из контекста
user, ok := middleware.GetUserFromContext(r.Context())
if !ok {
h.logger.Warn("get achievement by ID failed - authentication required")
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
// Получаем ID достижения из URL параметров
achievementIDStr := chi.URLParam(r, "id")
if achievementIDStr == "" {
utils.RespondWithError(w, http.StatusBadRequest, "Achievement ID is required")
return
}
achievementID, err := strconv.ParseUint(achievementIDStr, 10, 32)
if err != nil {
utils.RespondWithError(w, http.StatusBadRequest, "Invalid achievement ID")
return
}
achievement, err := h.achievementService.GetAchievementByID(uint(achievementID), user.ID)
if err != nil {
h.logger.Error("failed to get achievement by ID",
zap.Uint("user_id", user.ID),
zap.Uint("achievement_id", uint(achievementID)),
zap.Error(err),
)
if err == service.ErrAchievementNotFound {
utils.RespondWithError(w, http.StatusNotFound, "Achievement not found")
} else {
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get achievement: "+err.Error())
}
return
}
h.logger.Info("achievement retrieved successfully",
zap.Uint("user_id", user.ID),
zap.Uint("achievement_id", uint(achievementID)),
)
utils.RespondWithJSON(w, http.StatusOK, achievement)
}
// UpdateAchievement обновляет достижение
func (h *UserAchievementHandler) UpdateAchievement(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling update achievement request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Получаем пользователя из контекста
user, ok := middleware.GetUserFromContext(r.Context())
if !ok {
h.logger.Warn("update achievement failed - authentication required")
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
// Получаем ID достижения из URL параметров
achievementIDStr := chi.URLParam(r, "id")
if achievementIDStr == "" {
utils.RespondWithError(w, http.StatusBadRequest, "Achievement ID is required")
return
}
achievementID, err := strconv.ParseUint(achievementIDStr, 10, 32)
if err != nil {
utils.RespondWithError(w, http.StatusBadRequest, "Invalid achievement ID")
return
}
var req models.AchievementCreateRequest
if err := utils.DecodeJSONBody(w, r, &req); err != nil {
h.logger.Error("failed to decode achievement update request", zap.Error(err))
utils.RespondWithError(w, http.StatusBadRequest, "Invalid request payload: "+err.Error())
return
}
// Валидация запроса
if err := utils.ValidateStruct(req); err != nil {
h.logger.Warn("achievement update validation failed", zap.Error(err))
utils.RespondWithValidationError(w, err)
return
}
// Обновляем достижение через сервис
achievement, err := h.achievementService.UpdateAchievement(uint(achievementID), user.ID, req)
if err != nil {
h.logger.Error("failed to update achievement",
zap.Uint("user_id", user.ID),
zap.Uint("achievement_id", uint(achievementID)),
zap.Error(err),
)
switch err {
case service.ErrAchievementNotFound:
utils.RespondWithError(w, http.StatusNotFound, "Achievement not found")
case service.ErrAchievementAlreadyExists:
utils.RespondWithError(w, http.StatusConflict, "Achievement with this title already exists")
default:
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to update achievement: "+err.Error())
}
return
}
h.logger.Info("achievement updated successfully",
zap.Uint("user_id", user.ID),
zap.Uint("achievement_id", uint(achievementID)),
)
utils.RespondWithJSON(w, http.StatusOK, map[string]interface{}{
"message": "Achievement updated successfully",
"achievement": achievement,
})
}
// GetPublicUserAchievements возвращает достижения пользователя для публичного просмотра
func (h *UserAchievementHandler) GetPublicUserAchievements(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling get public user achievements request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Получаем userID из URL параметров
userIDStr := r.URL.Query().Get("userID")
if userIDStr == "" {
utils.RespondWithError(w, http.StatusBadRequest, "User ID is required")
return
}
userID, err := strconv.ParseUint(userIDStr, 10, 32)
if err != nil {
utils.RespondWithError(w, http.StatusBadRequest, "Invalid user ID")
return
}
// Получаем только подтвержденные достижения для публичного просмотра
achievements, err := h.achievementService.GetVerifiedAchievements(uint(userID))
if err != nil {
h.logger.Error("failed to get public user achievements",
zap.Uint("user_id", uint(userID)),
zap.Error(err),
)
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get achievements: "+err.Error())
return
}
h.logger.Info("public user achievements retrieved successfully",
zap.Uint("user_id", uint(userID)),
zap.Int("achievements_count", len(achievements)),
)
utils.RespondWithJSON(w, http.StatusOK, achievements)
}
// GetPublicUserAchievementsSummary возвращает сводку по достижениям пользователя для публичного просмотра
func (h *UserAchievementHandler) GetPublicUserAchievementsSummary(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling get public user achievements summary request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Получаем userID из URL параметров
userIDStr := r.URL.Query().Get("userID")
if userIDStr == "" {
utils.RespondWithError(w, http.StatusBadRequest, "User ID is required")
return
}
userID, err := strconv.ParseUint(userIDStr, 10, 32)
if err != nil {
utils.RespondWithError(w, http.StatusBadRequest, "Invalid user ID")
return
}
summary, err := h.achievementService.GetUserAchievementsSummary(uint(userID))
if err != nil {
h.logger.Error("failed to get public user achievements summary",
zap.Uint("user_id", uint(userID)),
zap.Error(err),
)
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get achievements summary: "+err.Error())
return
}
h.logger.Info("public user achievements summary retrieved successfully",
zap.Uint("user_id", uint(userID)),
zap.Int("total_achievements", summary.TotalAchievements),
zap.Int("completed", summary.Completed),
)
utils.RespondWithJSON(w, http.StatusOK, summary)
}
// GetPublicRecentAchievements возвращает последние достижения пользователя для публичного просмотра
func (h *UserAchievementHandler) GetPublicRecentAchievements(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling get public recent achievements request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Получаем userID из URL параметров
userIDStr := r.URL.Query().Get("userID")
if userIDStr == "" {
utils.RespondWithError(w, http.StatusBadRequest, "User ID is required")
return
}
userID, err := strconv.ParseUint(userIDStr, 10, 32)
if err != nil {
utils.RespondWithError(w, http.StatusBadRequest, "Invalid user ID")
return
}
// Получаем параметр limit из query string (по умолчанию 10)
limit := 10
limitStr := r.URL.Query().Get("limit")
if limitStr != "" {
if parsedLimit, err := strconv.Atoi(limitStr); err == nil && parsedLimit > 0 {
limit = parsedLimit
}
}
// Получаем только подтвержденные достижения
achievements, err := h.achievementService.GetVerifiedRecentAchievements(uint(userID), limit)
if err != nil {
h.logger.Error("failed to get public recent achievements",
zap.Uint("user_id", uint(userID)),
zap.Int("limit", limit),
zap.Error(err),
)
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get recent achievements: "+err.Error())
return
}
h.logger.Info("public recent achievements retrieved successfully",
zap.Uint("user_id", uint(userID)),
zap.Int("achievements_count", len(achievements)),
)
utils.RespondWithJSON(w, http.StatusOK, achievements)
}
// CreateAchievement создает новое достижение для пользователя
func (h *UserAchievementHandler) CreateAchievement(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling create achievement request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Получаем пользователя из контекста
user, ok := middleware.GetUserFromContext(r.Context())
if !ok {
h.logger.Warn("create achievement failed - authentication required")
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
var req models.AchievementCreateRequest
if err := utils.DecodeJSONBody(w, r, &req); err != nil {
h.logger.Error("failed to decode achievement request", zap.Error(err))
utils.RespondWithError(w, http.StatusBadRequest, "Invalid request payload: "+err.Error())
return
}
// Валидация запроса
if err := utils.ValidateStruct(req); err != nil {
h.logger.Warn("achievement validation failed", zap.Error(err))
utils.RespondWithValidationError(w, err)
return
}
// Создаем достижение через сервис
achievement, err := h.achievementService.CreateAchievement(user.ID, req)
if err != nil {
h.logger.Error("failed to create achievement",
zap.Uint("user_id", user.ID),
zap.Error(err),
)
if err == service.ErrAchievementAlreadyExists {
utils.RespondWithError(w, http.StatusConflict, "Achievement with this title already exists")
} else {
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to create achievement: "+err.Error())
}
return
}
h.logger.Info("achievement created successfully",
zap.Uint("user_id", user.ID),
zap.Uint("achievement_id", achievement.ID),
zap.String("title", achievement.Title),
)
utils.RespondWithJSON(w, http.StatusCreated, map[string]interface{}{
"message": "Achievement created successfully",
"achievement": achievement,
})
}
// GetUserAchievements возвращает все достижения пользователя
func (h *UserAchievementHandler) GetUserAchievements(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling get user achievements request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Получаем пользователя из контекста
user, ok := middleware.GetUserFromContext(r.Context())
if !ok {
h.logger.Warn("get achievements failed - authentication required")
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
achievements, err := h.achievementService.GetUserAchievements(user.ID)
if err != nil {
h.logger.Error("failed to get user achievements",
zap.Uint("user_id", user.ID),
zap.Error(err),
)
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get achievements: "+err.Error())
return
}
h.logger.Info("user achievements retrieved successfully",
zap.Uint("user_id", user.ID),
zap.Int("achievements_count", len(achievements)),
)
utils.RespondWithJSON(w, http.StatusOK, achievements)
}
// GetUserAchievementsSummary возвращает сводку по достижениям пользователя
func (h *UserAchievementHandler) GetUserAchievementsSummary(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling get user achievements summary request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Получаем пользователя из контекста
user, ok := middleware.GetUserFromContext(r.Context())
if !ok {
h.logger.Warn("get achievements summary failed - authentication required")
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
summary, err := h.achievementService.GetUserAchievementsSummary(user.ID)
if err != nil {
h.logger.Error("failed to get user achievements summary",
zap.Uint("user_id", user.ID),
zap.Error(err),
)
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get achievements summary: "+err.Error())
return
}
h.logger.Info("user achievements summary retrieved successfully",
zap.Uint("user_id", user.ID),
zap.Int("total_achievements", summary.TotalAchievements),
zap.Int("completed", summary.Completed),
zap.Float64("progress_percent", summary.ProgressPercent),
)
utils.RespondWithJSON(w, http.StatusOK, summary)
}
// GetRecentAchievements возвращает последние достижения пользователя
func (h *UserAchievementHandler) GetRecentAchievements(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling get recent achievements request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Получаем пользователя из контекста
user, ok := middleware.GetUserFromContext(r.Context())
if !ok {
h.logger.Warn("get recent achievements failed - authentication required")
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
// Получаем параметр limit из query string (по умолчанию 10)
limit := 10
limitStr := r.URL.Query().Get("limit")
if limitStr != "" {
if parsedLimit, err := strconv.Atoi(limitStr); err == nil && parsedLimit > 0 {
limit = parsedLimit
}
}
achievements, err := h.achievementService.GetRecentAchievements(user.ID, limit)
if err != nil {
h.logger.Error("failed to get recent achievements",
zap.Uint("user_id", user.ID),
zap.Int("limit", limit),
zap.Error(err),
)
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get recent achievements: "+err.Error())
return
}
h.logger.Info("recent achievements retrieved successfully",
zap.Uint("user_id", user.ID),
zap.Int("achievements_count", len(achievements)),
zap.Int("limit", limit),
)
utils.RespondWithJSON(w, http.StatusOK, achievements)
}
// VerifyAchievement подтверждает достижение пользователя
func (h *UserAchievementHandler) VerifyAchievement(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling verify achievement request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Получаем пользователя из контекста
user, ok := middleware.GetUserFromContext(r.Context())
if !ok {
h.logger.Warn("verify achievement failed - authentication required")
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
// Получаем ID достижения из URL параметров
achievementIDStr := r.URL.Query().Get("id")
if achievementIDStr == "" {
utils.RespondWithError(w, http.StatusBadRequest, "Achievement ID is required")
return
}
achievementID, err := strconv.ParseUint(achievementIDStr, 10, 32)
if err != nil {
utils.RespondWithError(w, http.StatusBadRequest, "Invalid achievement ID")
return
}
err = h.achievementService.VerifyAchievement(uint(achievementID), user.ID)
if err != nil {
h.logger.Error("failed to verify achievement",
zap.Uint("user_id", user.ID),
zap.Uint("achievement_id", uint(achievementID)),
zap.Error(err),
)
if err == service.ErrAchievementNotFound {
utils.RespondWithError(w, http.StatusNotFound, "Achievement not found")
} else {
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to verify achievement: "+err.Error())
}
return
}
h.logger.Info("achievement verified successfully",
zap.Uint("user_id", user.ID),
zap.Uint("achievement_id", uint(achievementID)),
)
utils.RespondWithJSON(w, http.StatusOK, map[string]string{
"message": "Achievement verified successfully",
})
}
// DeleteAchievement удаляет достижение пользователя
func (h *UserAchievementHandler) DeleteAchievement(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling delete achievement request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Получаем пользователя из контекста
user, ok := middleware.GetUserFromContext(r.Context())
if !ok {
h.logger.Warn("delete achievement failed - authentication required")
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
// Получаем ID достижения из URL параметров
achievementIDStr := r.URL.Query().Get("id")
if achievementIDStr == "" {
utils.RespondWithError(w, http.StatusBadRequest, "Achievement ID is required")
return
}
achievementID, err := strconv.ParseUint(achievementIDStr, 10, 32)
if err != nil {
utils.RespondWithError(w, http.StatusBadRequest, "Invalid achievement ID")
return
}
err = h.achievementService.DeleteAchievement(uint(achievementID), user.ID)
if err != nil {
h.logger.Error("failed to delete achievement",
zap.Uint("user_id", user.ID),
zap.Uint("achievement_id", uint(achievementID)),
zap.Error(err),
)
if err == service.ErrAchievementNotFound {
utils.RespondWithError(w, http.StatusNotFound, "Achievement not found")
} else {
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to delete achievement: "+err.Error())
}
return
}
h.logger.Info("achievement deleted successfully",
zap.Uint("user_id", user.ID),
zap.Uint("achievement_id", uint(achievementID)),
)
utils.RespondWithJSON(w, http.StatusOK, map[string]string{
"message": "Achievement deleted successfully",
})
}
@@ -0,0 +1,348 @@
// handlers/user_stats_handler.go
package handlers
import (
"net/http"
"strconv"
"api_bb/internal/service"
"api_bb/pkg/logger"
"api_bb/pkg/middleware"
"api_bb/pkg/utils"
"github.com/go-chi/chi/v5"
"go.uber.org/zap"
)
type UserStatsHandler struct {
logger logger.LoggerInterface
userStatsService service.UserStatsService
}
func NewUserStatsHandler(userStatsService service.UserStatsService) *UserStatsHandler {
return &UserStatsHandler{
logger: logger.NewWrapper(logger.Get().With(zap.String("handler", "user_stats"))),
userStatsService: userStatsService,
}
}
// GetUserStats возвращает статистику текущего пользователя
func (h *UserStatsHandler) GetUserStats(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling get user stats request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Получаем пользователя из контекста
user, ok := middleware.GetUserFromContext(r.Context())
if !ok {
h.logger.Warn("get user stats failed - authentication required")
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
// Получаем статистику через сервис
stats, err := h.userStatsService.GetUserStats(user.ID)
if err != nil {
h.logger.Error("failed to get user stats from service",
zap.Uint("user_id", user.ID),
zap.Error(err),
)
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get user stats: "+err.Error())
return
}
h.logger.Info("user stats retrieved successfully",
zap.Uint("user_id", user.ID),
zap.Float64("total_distance", stats.TotalDistance),
zap.Int("workouts_count", stats.WorkoutsCount),
)
utils.RespondWithJSON(w, http.StatusOK, stats)
}
// GetUserStatsByID возвращает статистику пользователя по ID (для администраторов)
func (h *UserStatsHandler) GetUserStatsByID(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling get user stats by ID request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Получаем текущего пользователя для проверки прав
currentUser, ok := middleware.GetUserFromContext(r.Context())
if !ok {
h.logger.Warn("get user stats by ID failed - authentication required")
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
// Проверяем права администратора
if currentUser.Role != "admin" {
h.logger.Warn("get user stats by ID failed - insufficient permissions",
zap.Uint("user_id", currentUser.ID),
zap.String("role", currentUser.Role),
)
utils.RespondWithError(w, http.StatusForbidden, "Insufficient permissions")
return
}
// Получаем ID пользователя из параметров URL
userIDStr := chi.URLParam(r, "userID")
userID, err := strconv.ParseUint(userIDStr, 10, 32)
if err != nil {
h.logger.Warn("invalid user ID parameter",
zap.String("user_id_param", userIDStr),
zap.Error(err),
)
utils.RespondWithError(w, http.StatusBadRequest, "Invalid user ID")
return
}
// Получаем статистику через сервис
stats, err := h.userStatsService.GetUserStats(uint(userID))
if err != nil {
h.logger.Error("failed to get user stats by ID from service",
zap.Uint("target_user_id", uint(userID)),
zap.Error(err),
)
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get user stats: "+err.Error())
return
}
h.logger.Info("user stats by ID retrieved successfully",
zap.Uint("admin_user_id", currentUser.ID),
zap.Uint("target_user_id", uint(userID)),
)
utils.RespondWithJSON(w, http.StatusOK, stats)
}
// UpdatePersonalBest обновляет личный рекорд пользователя
func (h *UserStatsHandler) UpdatePersonalBest(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling update personal best request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Получаем пользователя из контекста
user, ok := middleware.GetUserFromContext(r.Context())
if !ok {
h.logger.Warn("update personal best failed - authentication required")
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
var req struct {
DistanceType string `json:"distance_type"`
Time string `json:"time"`
}
if err := utils.DecodeJSONBody(w, r, &req); err != nil {
h.logger.Error("failed to decode update personal best request",
zap.Error(err),
)
utils.RespondWithError(w, http.StatusBadRequest, "Invalid request payload: "+err.Error())
return
}
// Валидация обязательных полей
if req.DistanceType == "" || req.Time == "" {
h.logger.Warn("update personal best failed - missing required fields")
utils.RespondWithError(w, http.StatusBadRequest, "Distance type and time are required")
return
}
// Валидация типа дистанции
validDistanceTypes := map[string]bool{
"5k": true, "10k": true, "half": true, "marathon": true,
}
if !validDistanceTypes[req.DistanceType] {
h.logger.Warn("update personal best failed - invalid distance type",
zap.String("distance_type", req.DistanceType),
)
utils.RespondWithError(w, http.StatusBadRequest, "Invalid distance type. Must be: 5k, 10k, half, marathon")
return
}
h.logger.Info("updating personal best",
zap.Uint("user_id", user.ID),
zap.String("distance_type", req.DistanceType),
zap.String("time", req.Time),
)
// Обновляем личный рекорд через сервис
if err := h.userStatsService.UpdatePersonalBest(user.ID, req.DistanceType, req.Time); err != nil {
h.logger.Error("failed to update personal best in service",
zap.Uint("user_id", user.ID),
zap.Error(err),
)
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to update personal best: "+err.Error())
return
}
h.logger.Info("personal best updated successfully",
zap.Uint("user_id", user.ID),
zap.String("distance_type", req.DistanceType),
)
utils.RespondWithJSON(w, http.StatusOK, map[string]interface{}{
"message": "Personal best updated successfully",
"distance_type": req.DistanceType,
"time": req.Time,
})
}
// IncrementWorkout увеличивает счетчик тренировок и обновляет статистику
func (h *UserStatsHandler) IncrementWorkout(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling increment workout request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Получаем пользователя из контекста
user, ok := middleware.GetUserFromContext(r.Context())
if !ok {
h.logger.Warn("increment workout failed - authentication required")
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
var req struct {
Distance float64 `json:"distance"`
Duration int `json:"duration"`
}
if err := utils.DecodeJSONBody(w, r, &req); err != nil {
h.logger.Error("failed to decode increment workout request",
zap.Error(err),
)
utils.RespondWithError(w, http.StatusBadRequest, "Invalid request payload: "+err.Error())
return
}
// Валидация данных тренировки
if req.Distance <= 0 {
h.logger.Warn("increment workout failed - invalid distance",
zap.Float64("distance", req.Distance),
)
utils.RespondWithError(w, http.StatusBadRequest, "Distance must be greater than 0")
return
}
if req.Duration <= 0 {
h.logger.Warn("increment workout failed - invalid duration",
zap.Int("duration", req.Duration),
)
utils.RespondWithError(w, http.StatusBadRequest, "Duration must be greater than 0")
return
}
h.logger.Info("incrementing workout stats",
zap.Uint("user_id", user.ID),
zap.Float64("distance", req.Distance),
zap.Int("duration", req.Duration),
)
// Обновляем статистику через сервис
if err := h.userStatsService.IncrementWorkout(user.ID, req.Distance, req.Duration); err != nil {
h.logger.Error("failed to increment workout in service",
zap.Uint("user_id", user.ID),
zap.Error(err),
)
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to update workout stats: "+err.Error())
return
}
h.logger.Info("workout stats incremented successfully",
zap.Uint("user_id", user.ID),
zap.Float64("distance", req.Distance),
zap.Int("duration", req.Duration),
)
utils.RespondWithJSON(w, http.StatusOK, map[string]interface{}{
"message": "Workout stats updated successfully",
"distance": req.Distance,
"duration": req.Duration,
})
}
// ResetWeeklyDistance сбрасывает недельный пробег
func (h *UserStatsHandler) ResetWeeklyDistance(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling reset weekly distance request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Получаем пользователя из контекста
user, ok := middleware.GetUserFromContext(r.Context())
if !ok {
h.logger.Warn("reset weekly distance failed - authentication required")
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
h.logger.Info("resetting weekly distance",
zap.Uint("user_id", user.ID),
)
// Сбрасываем недельный пробег через сервис
if err := h.userStatsService.ResetWeeklyDistance(user.ID); err != nil {
h.logger.Error("failed to reset weekly distance in service",
zap.Uint("user_id", user.ID),
zap.Error(err),
)
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to reset weekly distance: "+err.Error())
return
}
h.logger.Info("weekly distance reset successfully",
zap.Uint("user_id", user.ID),
)
utils.RespondWithJSON(w, http.StatusOK, map[string]interface{}{
"message": "Weekly distance reset successfully",
})
}
// ResetMonthlyDistance сбрасывает месячный пробег
func (h *UserStatsHandler) ResetMonthlyDistance(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling reset monthly distance request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Получаем пользователя из контекста
user, ok := middleware.GetUserFromContext(r.Context())
if !ok {
h.logger.Warn("reset monthly distance failed - authentication required")
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
h.logger.Info("resetting monthly distance",
zap.Uint("user_id", user.ID),
)
// Сбрасываем месячный пробег через сервис
if err := h.userStatsService.ResetMonthlyDistance(user.ID); err != nil {
h.logger.Error("failed to reset monthly distance in service",
zap.Uint("user_id", user.ID),
zap.Error(err),
)
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to reset monthly distance: "+err.Error())
return
}
h.logger.Info("monthly distance reset successfully",
zap.Uint("user_id", user.ID),
)
utils.RespondWithJSON(w, http.StatusOK, map[string]interface{}{
"message": "Monthly distance reset successfully",
})
}
@@ -0,0 +1,374 @@
// handlers/user_workout_handler.go
package handlers
import (
"encoding/json"
"net/http"
"strconv"
"api_bb/internal/models"
"api_bb/internal/service"
"api_bb/pkg/logger"
"api_bb/pkg/middleware"
"api_bb/pkg/utils"
"go.uber.org/zap"
"github.com/go-chi/chi/v5"
)
type UserWorkoutHandler struct {
logger logger.LoggerInterface
workoutService service.WorkoutService
}
func NewUserWorkoutHandler(workoutService service.WorkoutService) *UserWorkoutHandler {
return &UserWorkoutHandler{
logger: logger.NewWrapper(logger.Get().With(zap.String("handler", "user_workout"))),
workoutService: workoutService,
}
}
// CreateWorkout создает новую тренировку
func (h *UserWorkoutHandler) CreateWorkout(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling create workout request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Получаем пользователя из контекста
user, ok := middleware.GetUserFromContext(r.Context())
if !ok {
h.logger.Warn("create workout failed - authentication required")
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
var req models.WorkoutCreateRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
h.logger.Error("failed to decode JSON payload", zap.Error(err))
utils.RespondWithError(w, http.StatusBadRequest, "Invalid request payload: "+err.Error())
return
}
// Валидация
if err := utils.ValidateStruct(req); err != nil {
h.logger.Warn("create workout failed - validation error", zap.Error(err))
utils.RespondWithError(w, http.StatusBadRequest, "Validation error: "+err.Error())
return
}
h.logger.Info("creating new workout",
zap.Uint("user_id", user.ID),
zap.String("type", string(req.Type)),
zap.Float64("distance", req.Distance),
zap.Int("duration", req.Duration),
)
// Создаем тренировку
workout, err := h.workoutService.CreateWorkout(user.ID, &req)
if err != nil {
h.logger.Error("failed to create workout in service",
zap.Uint("user_id", user.ID),
zap.Error(err),
)
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to create workout: "+err.Error())
return
}
h.logger.Info("workout created successfully",
zap.Uint("workout_id", workout.ID),
zap.Uint("user_id", user.ID),
)
utils.RespondWithJSON(w, http.StatusCreated, map[string]interface{}{
"message": "Workout created successfully",
"workout": workout,
})
}
// GetWorkouts возвращает список тренировок пользователя
func (h *UserWorkoutHandler) GetWorkouts(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling get workouts request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Получаем пользователя из контекста
user, ok := middleware.GetUserFromContext(r.Context())
if !ok {
h.logger.Warn("get workouts failed - authentication required")
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
workouts, err := h.workoutService.GetUserWorkouts(user.ID)
if err != nil {
h.logger.Error("failed to get user workouts from service",
zap.Uint("user_id", user.ID),
zap.Error(err),
)
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get workouts: "+err.Error())
return
}
h.logger.Info("user workouts retrieved successfully",
zap.Uint("user_id", user.ID),
zap.Int("workouts_count", len(workouts)),
)
utils.RespondWithJSON(w, http.StatusOK, workouts)
}
// GetWorkoutByID возвращает тренировку по ID
func (h *UserWorkoutHandler) GetWorkoutByID(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling get workout by ID request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Получаем пользователя из контекста
user, ok := middleware.GetUserFromContext(r.Context())
if !ok {
h.logger.Warn("get workout failed - authentication required")
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
// Получаем ID тренировки из URL параметров
workoutIDStr := chi.URLParam(r, "id")
workoutID, err := strconv.ParseUint(workoutIDStr, 10, 32)
if err != nil {
h.logger.Warn("invalid workout ID", zap.String("workout_id", workoutIDStr))
utils.RespondWithError(w, http.StatusBadRequest, "Invalid workout ID")
return
}
workout, err := h.workoutService.GetWorkoutByID(user.ID, uint(workoutID))
if err != nil {
h.logger.Error("failed to get workout from service",
zap.Uint("user_id", user.ID),
zap.Uint("workout_id", uint(workoutID)),
zap.Error(err),
)
utils.RespondWithError(w, http.StatusNotFound, "Workout not found: "+err.Error())
return
}
h.logger.Info("workout retrieved successfully",
zap.Uint("user_id", user.ID),
zap.Uint("workout_id", uint(workoutID)),
)
utils.RespondWithJSON(w, http.StatusOK, workout)
}
// UpdateWorkout обновляет тренировку
func (h *UserWorkoutHandler) UpdateWorkout(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling update workout request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Получаем пользователя из контекста
user, ok := middleware.GetUserFromContext(r.Context())
if !ok {
h.logger.Warn("update workout failed - authentication required")
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
// Получаем ID тренировки из URL параметров
workoutIDStr := chi.URLParam(r, "id")
workoutID, err := strconv.ParseUint(workoutIDStr, 10, 32)
if err != nil {
h.logger.Warn("invalid workout ID", zap.String("workout_id", workoutIDStr))
utils.RespondWithError(w, http.StatusBadRequest, "Invalid workout ID")
return
}
var req models.WorkoutUpdateRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
h.logger.Error("failed to decode JSON payload", zap.Error(err))
utils.RespondWithError(w, http.StatusBadRequest, "Invalid request payload: "+err.Error())
return
}
// Валидация
if err := utils.ValidateStruct(req); err != nil {
h.logger.Warn("update workout failed - validation error", zap.Error(err))
utils.RespondWithError(w, http.StatusBadRequest, "Validation error: "+err.Error())
return
}
h.logger.Info("updating workout",
zap.Uint("user_id", user.ID),
zap.Uint("workout_id", uint(workoutID)),
zap.String("type", string(req.Type)),
)
// Обновляем тренировку
workout, err := h.workoutService.UpdateWorkout(user.ID, uint(workoutID), &req)
if err != nil {
h.logger.Error("failed to update workout in service",
zap.Uint("user_id", user.ID),
zap.Uint("workout_id", uint(workoutID)),
zap.Error(err),
)
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to update workout: "+err.Error())
return
}
h.logger.Info("workout updated successfully",
zap.Uint("user_id", user.ID),
zap.Uint("workout_id", uint(workoutID)),
)
utils.RespondWithJSON(w, http.StatusOK, map[string]interface{}{
"message": "Workout updated successfully",
"workout": workout,
})
}
// DeleteWorkout удаляет тренировку
func (h *UserWorkoutHandler) DeleteWorkout(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling delete workout request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Получаем пользователя из контекста
user, ok := middleware.GetUserFromContext(r.Context())
if !ok {
h.logger.Warn("delete workout failed - authentication required")
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
// Получаем ID тренировки из URL параметров
workoutIDStr := chi.URLParam(r, "id")
workoutID, err := strconv.ParseUint(workoutIDStr, 10, 32)
if err != nil {
h.logger.Warn("invalid workout ID", zap.String("workout_id", workoutIDStr))
utils.RespondWithError(w, http.StatusBadRequest, "Invalid workout ID")
return
}
h.logger.Info("deleting workout",
zap.Uint("user_id", user.ID),
zap.Uint("workout_id", uint(workoutID)),
)
// Удаляем тренировку
if err := h.workoutService.DeleteWorkout(user.ID, uint(workoutID)); err != nil {
h.logger.Error("failed to delete workout in service",
zap.Uint("user_id", user.ID),
zap.Uint("workout_id", uint(workoutID)),
zap.Error(err),
)
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to delete workout: "+err.Error())
return
}
h.logger.Info("workout deleted successfully",
zap.Uint("user_id", user.ID),
zap.Uint("workout_id", uint(workoutID)),
)
utils.RespondWithJSON(w, http.StatusOK, map[string]string{
"message": "Workout deleted successfully",
})
}
// GetWorkoutStats возвращает статистику тренировок
func (h *UserWorkoutHandler) GetWorkoutStats(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling get workout stats request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Получаем пользователя из контекста
user, ok := middleware.GetUserFromContext(r.Context())
if !ok {
h.logger.Warn("get workout stats failed - authentication required")
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
stats, err := h.workoutService.GetWorkoutStats(user.ID)
if err != nil {
h.logger.Error("failed to get workout stats from service",
zap.Uint("user_id", user.ID),
zap.Error(err),
)
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get workout stats: "+err.Error())
return
}
h.logger.Info("workout stats retrieved successfully",
zap.Uint("user_id", user.ID),
zap.Int("total_workouts", stats.TotalWorkouts),
zap.Float64("total_distance", stats.TotalDistance),
)
utils.RespondWithJSON(w, http.StatusOK, stats)
}
// GetWorkoutsByType возвращает тренировки по типу
func (h *UserWorkoutHandler) GetWorkoutsByType(w http.ResponseWriter, r *http.Request) {
h.logger.Info("handling get workouts by type request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
// Получаем пользователя из контекста
user, ok := middleware.GetUserFromContext(r.Context())
if !ok {
h.logger.Warn("get workouts by type failed - authentication required")
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
// Получаем тип тренировки из URL параметров
workoutType := models.WorkoutType(chi.URLParam(r, "type"))
// Валидация типа тренировки
validTypes := map[models.WorkoutType]bool{
models.WorkoutTypeEasy: true,
models.WorkoutTypeTempo: true,
models.WorkoutTypeInterval: true,
models.WorkoutTypeLong: true,
models.WorkoutTypeRecovery: true,
}
if !validTypes[workoutType] {
h.logger.Warn("invalid workout type", zap.String("type", string(workoutType)))
utils.RespondWithError(w, http.StatusBadRequest, "Invalid workout type")
return
}
workouts, err := h.workoutService.GetWorkoutsByType(user.ID, workoutType)
if err != nil {
h.logger.Error("failed to get workouts by type from service",
zap.Uint("user_id", user.ID),
zap.String("type", string(workoutType)),
zap.Error(err),
)
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get workouts: "+err.Error())
return
}
h.logger.Info("workouts by type retrieved successfully",
zap.Uint("user_id", user.ID),
zap.String("type", string(workoutType)),
zap.Int("workouts_count", len(workouts)),
)
utils.RespondWithJSON(w, http.StatusOK, workouts)
}
@@ -0,0 +1,72 @@
// models/achievement.go
package models
import (
"time"
"gorm.io/gorm"
)
type AchievementType string
const (
AchievementTypeDistance AchievementType = "distance"
AchievementTypeSpeed AchievementType = "speed"
AchievementTypeConsistency AchievementType = "consistency"
AchievementTypeEvent AchievementType = "event"
AchievementTypeSpecial AchievementType = "special"
)
type Achievement struct {
ID uint `json:"id" gorm:"primaryKey"`
UserID uint `json:"user_id" gorm:"not null;index"`
Type AchievementType `json:"type" gorm:"type:varchar(20);not null"`
Title string `json:"title" gorm:"size:255;not null"`
Description string `json:"description" gorm:"type:text"`
Result string `json:"result" gorm:"size:100"` // Достигнутый результат
Distance string `json:"distance" gorm:"size:50"` // Дистанция достижения
Date time.Time `json:"date" gorm:"not null"`
Verified bool `json:"verified" gorm:"default:false"`
BadgeImage string `json:"badge_image" gorm:"size:500"` // Изображение бейджа
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
// Связи
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
}
// BeforeCreate hook
func (a *Achievement) BeforeCreate(tx *gorm.DB) error {
if a.CreatedAt.IsZero() {
a.CreatedAt = time.Now()
}
if a.UpdatedAt.IsZero() {
a.UpdatedAt = time.Now()
}
return nil
}
// BeforeUpdate hook
func (a *Achievement) BeforeUpdate(tx *gorm.DB) error {
a.UpdatedAt = time.Now()
return nil
}
// DTO для создания достижения
type AchievementCreateRequest struct {
Type AchievementType `json:"type" validate:"required,oneof=distance speed consistency event special"`
Title string `json:"title" validate:"required,min=5,max=255"`
Description string `json:"description" validate:"max=1000"`
Result string `json:"result" validate:"max=100"`
Distance string `json:"distance" validate:"max=50"`
Date time.Time `json:"date" validate:"required"`
BadgeImage string `json:"badge_image" validate:"max=500"`
}
// DTO для ответа с достижениями пользователя
type UserAchievementsResponse struct {
TotalAchievements int `json:"total_achievements"`
Completed int `json:"completed"`
ProgressPercent float64 `json:"progress_percent"`
Achievements []Achievement `json:"achievements"`
}
@@ -0,0 +1,38 @@
// models/common.go
package models
import "time"
// Общая структура для информации об авторе
type AuthorInfo struct {
ID uint `json:"id"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Avatar string `json:"avatar,omitempty"`
Email string `json:"email,omitempty"` // Добавляем email
}
// DTO для пагинации
type PaginationRequest struct {
Page int `form:"page" validate:"min=1" default:"1"`
PerPage int `form:"per_page" validate:"min=1,max=100" default:"10"`
}
type PaginationResponse struct {
Page int `json:"page"`
PerPage int `json:"per_page"`
Total int `json:"total"`
TotalPages int `json:"total_pages"`
}
// DTO для фильтров
type DateRangeFilter struct {
StartDate *time.Time `form:"start_date"`
EndDate *time.Time `form:"end_date"`
}
type WorkoutFilter struct {
DateRangeFilter
Type string `form:"type"`
UserID uint `form:"user_id"`
}
@@ -0,0 +1,33 @@
// models/email.go
package models
import (
"time"
"gorm.io/gorm"
)
type EmailVerification struct {
ID uint `json:"id" gorm:"primaryKey"`
UserID uint `json:"user_id" gorm:"not null;index"`
Token string `json:"token" gorm:"size:100;not null;uniqueIndex"`
Email string `json:"email" gorm:"not null"`
Type string `json:"type" gorm:"size:20;not null"` // verification, password_reset
ExpiresAt time.Time `json:"expires_at" gorm:"not null"`
Used bool `json:"used" gorm:"default:false"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
// Связи
User User `gorm:"foreignKey:UserID" json:"user,omitempty"`
}
type PasswordResetRequest struct {
Email string `json:"email" validate:"required,email"`
}
type PasswordResetConfirm struct {
Token string `json:"token" validate:"required"`
Password string `json:"password" validate:"required,min=6"`
}
@@ -0,0 +1,49 @@
// models/event.go
package models
import (
"time"
)
type EventType string
const (
EventTypeRace EventType = "race"
EventTypeTraining EventType = "training"
EventTypeSocial EventType = "social"
EventTypeWorkshop EventType = "workshop"
)
type Event struct {
ID uint `gorm:"primaryKey" json:"id"`
Title string `gorm:"size:255;not null" json:"title" validate:"required,min=5,max=255"`
Description string `gorm:"type:text;not null" json:"description" validate:"required,min=10"`
Date time.Time `gorm:"not null" json:"date" validate:"required"`
Location string `gorm:"size:255;not null" json:"location" validate:"required,max=255"`
Type EventType `gorm:"size:50;not null" json:"type" validate:"required,oneof=race training social workshop"`
Distance string `gorm:"size:50" json:"distance" validate:"max=50"`
ParticipantsCount int `gorm:"default:0" json:"participants_count"`
MaxParticipants int `gorm:"default:0" json:"max_participants" validate:"min=0"`
RegistrationOpen bool `gorm:"default:true" json:"registration_open"`
Image string `gorm:"size:500" json:"image" validate:"max=500"`
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
// Связи
Registrations []EventRegistration `gorm:"foreignKey:EventID" json:"registrations,omitempty"`
}
type EventRegistration struct {
ID uint `gorm:"primaryKey" json:"id"`
UserID uint `gorm:"not null" json:"user_id"`
EventID uint `gorm:"not null" json:"event_id"`
Status string `gorm:"size:50;default:pending" json:"status" validate:"oneof=pending confirmed cancelled completed"`
Notes string `gorm:"type:text" json:"notes" validate:"max=500"`
ResultTime string `gorm:"size:20" json:"result_time" validate:"max=20"`
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
// Связи
User User `gorm:"foreignKey:UserID" json:"user,omitempty"`
Event *Event `gorm:"foreignKey:EventID" json:"event,omitempty"`
}
@@ -0,0 +1,74 @@
// models/gallery.go
package models
import (
"time"
"gorm.io/gorm"
)
type GalleryCategory string
const (
GalleryCategoryTraining GalleryCategory = "training"
GalleryCategoryEvents GalleryCategory = "events"
GalleryCategoryCommunity GalleryCategory = "community"
GalleryCategoryAchievements GalleryCategory = "achievements"
)
type Gallery struct {
ID uint `json:"id" gorm:"primaryKey"`
Title string `json:"title" gorm:"size:255;not null"`
Description string `json:"description" gorm:"type:text"`
ImagePath string `json:"image_path" gorm:"size:500;not null"` // Путь к изображению
Category GalleryCategory `json:"category" gorm:"type:varchar(20);not null"`
AuthorID uint `json:"author_id" gorm:"not null;index"`
EventDate *time.Time `json:"event_date"` // Дата события на фото
Views int `json:"views" gorm:"default:0"`
Likes int `json:"likes" gorm:"default:0"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
// Связи
Author User `json:"author,omitempty" gorm:"foreignKey:AuthorID"`
}
// BeforeCreate hook
func (g *Gallery) BeforeCreate(tx *gorm.DB) error {
if g.CreatedAt.IsZero() {
g.CreatedAt = time.Now()
}
if g.UpdatedAt.IsZero() {
g.UpdatedAt = time.Now()
}
return nil
}
// BeforeUpdate hook
func (g *Gallery) BeforeUpdate(tx *gorm.DB) error {
g.UpdatedAt = time.Now()
return nil
}
// DTO для создания записи в галерее
type GalleryCreateRequest struct {
Title string `json:"title" validate:"required,min=5,max=255"`
Description string `json:"description" validate:"max=1000"`
ImagePath string `json:"image_path" validate:"required,max=500"`
Category GalleryCategory `json:"category" validate:"required,oneof=training events community achievements"`
EventDate *time.Time `json:"event_date"`
}
// DTO для ответа с галереей
type GalleryResponse struct {
ID uint `json:"id"`
Title string `json:"title"`
Description string `json:"description"`
ImagePath string `json:"image_path"`
Category GalleryCategory `json:"category"`
EventDate *time.Time `json:"event_date"`
Views int `json:"views"`
Likes int `json:"likes"`
CreatedAt time.Time `json:"created_at"`
Author AuthorInfo `json:"author"`
}
+93
View File
@@ -0,0 +1,93 @@
package models
import (
"time"
"gorm.io/gorm"
)
type NewsCategory string
const (
NewsCategoryEvents NewsCategory = "events"
NewsCategoryTraining NewsCategory = "training"
NewsCategoryAchievements NewsCategory = "achievements"
NewsCategoryCommunity NewsCategory = "community"
)
type News struct {
ID uint `json:"id" gorm:"primarykey"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt gorm.DeletedAt `json:"deleted_at,omitempty" gorm:"index"`
Title string `json:"title" gorm:"size:255;not null"`
Excerpt string `json:"excerpt" gorm:"size:500;not null"`
Content string `json:"content" gorm:"type:text;not null"`
Image string `json:"image" gorm:"size:255"`
Category NewsCategory `json:"category" gorm:"type:varchar(20);not null"`
Views int `json:"views" gorm:"default:0"`
// Связи
AuthorID uint `json:"author_id" gorm:"not null"`
Author User `json:"author" gorm:"foreignKey:AuthorID"`
Comments []Comment `json:"comments,omitempty" gorm:"foreignKey:NewsID"`
}
type Comment struct {
ID uint `json:"id" gorm:"primarykey"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
Content string `json:"content" gorm:"type:text;not null"`
// Связи
NewsID uint `json:"news_id" gorm:"not null"`
AuthorID uint `json:"author_id" gorm:"not null"`
Author User `json:"author" gorm:"foreignKey:AuthorID"`
}
// DTO для создания новости
type CreateNewsRequest struct {
Title string `json:"title" validate:"required,min=5,max=255"`
Excerpt string `json:"excerpt" validate:"required,min=10,max=500"`
Content string `json:"content" validate:"required,min=50"`
Image string `json:"image"`
Category NewsCategory `json:"category" validate:"required,oneof=events training achievements community"`
}
// DTO для обновления новости
type UpdateNewsRequest struct {
Title string `json:"title" validate:"omitempty,min=5,max=255"`
Excerpt string `json:"excerpt" validate:"omitempty,min=10,max=500"`
Content string `json:"content" validate:"omitempty,min=50"`
Image string `json:"image"`
Category NewsCategory `json:"category" validate:"omitempty,oneof=events training achievements community"`
}
// DTO для ответа с новостью
type NewsResponse struct {
ID uint `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
Title string `json:"title"`
Excerpt string `json:"excerpt"`
Content string `json:"content"`
Image string `json:"image"`
Category NewsCategory `json:"category"`
Views int `json:"views"`
Author AuthorInfo `json:"author"`
Comments int `json:"comments_count"`
}
// DTO для комментария
type CreateCommentRequest struct {
Content string `json:"content" validate:"required,min=1,max=1000"`
}
type CommentResponse struct {
ID uint `json:"id"`
CreatedAt time.Time `json:"created_at"`
Content string `json:"content"`
Author AuthorInfo `json:"author"`
}
@@ -0,0 +1,85 @@
// models/personal_best.go
package models
import (
"time"
"gorm.io/gorm"
)
type DistanceType string
const (
Distance5K DistanceType = "5k"
Distance10K DistanceType = "10k"
DistanceHalf DistanceType = "half_marathon"
DistanceFull DistanceType = "marathon"
DistanceOther DistanceType = "other"
)
type PersonalBest struct {
ID uint `json:"id" gorm:"primaryKey"`
UserID uint `json:"user_id" gorm:"not null;index"`
DistanceType DistanceType `json:"distance_type" gorm:"type:varchar(20);not null"`
Time string `json:"time" gorm:"size:20;not null"` // Время в формате "HH:MM:SS"
Pace string `json:"pace" gorm:"size:20"` // Темп
Date time.Time `json:"date" gorm:"not null"`
Verified bool `json:"verified" gorm:"default:false"` // Подтвержден ли результат
EventName string `json:"event_name" gorm:"size:255"` // Название забега
Location string `json:"location" gorm:"size:255"` // Место проведения
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
// Связи
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
}
// BeforeCreate hook
func (pb *PersonalBest) BeforeCreate(tx *gorm.DB) error {
if pb.CreatedAt.IsZero() {
pb.CreatedAt = time.Now()
}
if pb.UpdatedAt.IsZero() {
pb.UpdatedAt = time.Now()
}
return nil
}
// BeforeUpdate hook
func (pb *PersonalBest) BeforeUpdate(tx *gorm.DB) error {
pb.UpdatedAt = time.Now()
return nil
}
// DTO для создания личного рекорда
type PersonalBestCreateRequest struct {
DistanceType DistanceType `json:"distance_type" validate:"required,oneof=5k 10k half_marathon marathon other"`
Time string `json:"time" validate:"required,max=20"`
Pace string `json:"pace" validate:"max=20"`
Date time.Time `json:"date" validate:"required"`
EventName string `json:"event_name" validate:"max=255"`
Location string `json:"location" validate:"max=255"`
}
// DTO для обновления личного рекорда
type PersonalBestUpdateRequest struct {
DistanceType DistanceType `json:"distance_type" validate:"omitempty,oneof=5k 10k half_marathon marathon other"`
Time string `json:"time" validate:"omitempty,max=20"`
Pace string `json:"pace" validate:"omitempty,max=20"`
Date time.Time `json:"date"`
EventName string `json:"event_name" validate:"omitempty,max=255"`
Location string `json:"location" validate:"omitempty,max=255"`
Verified bool `json:"verified"`
}
// PersonalBestsSummary представляет сводку лучших результатов по дистанциям
type PersonalBestsSummary struct {
Best5K string `json:"best_5k,omitempty"`
Best5KPace string `json:"best_5k_pace,omitempty"`
Best10K string `json:"best_10k,omitempty"`
Best10KPace string `json:"best_10k_pace,omitempty"`
BestHalf string `json:"best_half_marathon,omitempty"`
BestHalfPace string `json:"best_half_marathon_pace,omitempty"`
BestMarathon string `json:"best_marathon,omitempty"`
BestMarathonPace string `json:"best_marathon_pace,omitempty"`
}
@@ -0,0 +1,69 @@
// models/review.go
package models
import (
"time"
"gorm.io/gorm"
)
type Review struct {
ID uint `json:"id" gorm:"primarykey"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt gorm.DeletedAt `json:"deleted_at,omitempty" gorm:"index"`
Rating int `json:"rating" gorm:"not null;check:rating >= 1 AND rating <= 5"`
Text string `json:"text" gorm:"type:text;not null"`
Achievement string `json:"achievement" gorm:"size:255"`
Distance string `json:"distance" gorm:"size:50"`
Improvement string `json:"improvement" gorm:"size:100"`
Trainings int `json:"trainings" gorm:"default:0"`
Verified bool `json:"verified" gorm:"default:false"`
// Связи
AuthorID uint `json:"author_id" gorm:"not null"`
Author User `json:"author" gorm:"foreignKey:AuthorID"`
}
// DTO для создания отзыва
type CreateReviewRequest struct {
Rating int `json:"rating" validate:"required,min=1,max=5"`
Text string `json:"text" validate:"required,min=10,max=500"`
Achievement string `json:"achievement" validate:"max=255"`
Distance string `json:"distance" validate:"max=50"`
Improvement string `json:"improvement" validate:"max=100"`
Trainings int `json:"trainings" validate:"min=0"`
}
// DTO для обновления отзыва
type UpdateReviewRequest struct {
Rating int `json:"rating" validate:"omitempty,min=1,max=5"`
Text string `json:"text" validate:"omitempty,min=10,max=500"`
Achievement string `json:"achievement" validate:"omitempty,max=255"`
Distance string `json:"distance" validate:"omitempty,max=50"`
Improvement string `json:"improvement" validate:"omitempty,max=100"`
Trainings int `json:"trainings" validate:"omitempty,min=0"`
}
// DTO для ответа с отзывом
type ReviewResponse struct {
ID uint `json:"id"`
CreatedAt time.Time `json:"created_at"`
Rating int `json:"rating"`
Text string `json:"text"`
Achievement string `json:"achievement,omitempty"`
Distance string `json:"distance,omitempty"`
Improvement string `json:"improvement,omitempty"`
Trainings int `json:"trainings"`
Verified bool `json:"verified"`
Author AuthorInfo `json:"author"`
}
// DTO для статистики отзывов
type ReviewsStatsResponse struct {
TotalReviews int `json:"total_reviews"`
AverageRating float64 `json:"average_rating"`
SuccessStories int `json:"success_stories"`
RatingDistribution map[int]int `json:"rating_distribution"`
}
@@ -0,0 +1,85 @@
// models/training_plan.go
package models
import (
"time"
"gorm.io/gorm"
)
type TrainingPlan struct {
ID uint `json:"id" gorm:"primaryKey"`
UserID uint `json:"user_id" gorm:"not null;index"`
Title string `json:"title" gorm:"size:255;not null"`
Description string `json:"description" gorm:"type:text"`
Weeks int `json:"weeks" gorm:"not null;default:12"` // Длительность плана в неделях
WorkoutsPerWeek int `json:"workouts_per_week" gorm:"not null;default:3"` // Тренировок в неделю
TargetDistance string `json:"target_distance" gorm:"size:50"` // Целевая дистанция
TargetDate time.Time `json:"target_date"` // Дата цели
CurrentWeek int `json:"current_week" gorm:"default:1"` // Текущая неделя
Completed bool `json:"completed" gorm:"default:false"` // Завершен ли план
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
// Связи
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
Workouts []TrainingWorkout `json:"workouts,omitempty" gorm:"foreignKey:PlanID"`
}
type TrainingWorkout struct {
ID uint `json:"id" gorm:"primaryKey"`
PlanID uint `json:"plan_id" gorm:"not null;index"`
Week int `json:"week" gorm:"not null"` // Неделя плана
Day int `json:"day" gorm:"not null"` // День недели (1-7)
Type WorkoutType `json:"type" gorm:"type:varchar(20);not null"`
Description string `json:"description" gorm:"type:text"`
Distance float64 `json:"distance_km" gorm:"type:decimal(5,2)"`
Duration int `json:"duration_min"`
Completed bool `json:"completed" gorm:"default:false"`
CompletedAt *time.Time `json:"completed_at"`
CreatedAt time.Time `json:"created_at"`
}
// BeforeCreate hooks
func (tp *TrainingPlan) BeforeCreate(tx *gorm.DB) error {
if tp.CreatedAt.IsZero() {
tp.CreatedAt = time.Now()
}
if tp.UpdatedAt.IsZero() {
tp.UpdatedAt = time.Now()
}
return nil
}
func (tw *TrainingWorkout) BeforeCreate(tx *gorm.DB) error {
if tw.CreatedAt.IsZero() {
tw.CreatedAt = time.Now()
}
return nil
}
// BeforeUpdate hook
func (tp *TrainingPlan) BeforeUpdate(tx *gorm.DB) error {
tp.UpdatedAt = time.Now()
return nil
}
// DTO для создания плана тренировок
type TrainingPlanCreateRequest struct {
Title string `json:"title" validate:"required,min=5,max=255"`
Description string `json:"description" validate:"max=1000"`
Weeks int `json:"weeks" validate:"required,min=1,max=52"`
WorkoutsPerWeek int `json:"workouts_per_week" validate:"required,min=1,max=7"`
TargetDistance string `json:"target_distance" validate:"max=50"`
TargetDate time.Time `json:"target_date"`
}
// DTO для обновления плана тренировок
type TrainingPlanUpdateRequest struct {
Title string `json:"title" validate:"min=5,max=255"`
Description string `json:"description" validate:"max=1000"`
Weeks int `json:"weeks" validate:"min=1,max=52"`
WorkoutsPerWeek int `json:"workouts_per_week" validate:"min=1,max=7"`
TargetDistance string `json:"target_distance" validate:"max=50"`
TargetDate time.Time `json:"target_date"`
}
+116
View File
@@ -0,0 +1,116 @@
// models/user.go
package models
import (
"time"
"golang.org/x/crypto/bcrypt"
"gorm.io/gorm"
)
// models/user.go - добавить поле Avatar
type User struct {
ID uint `json:"id" gorm:"primaryKey"`
Email string `json:"email" gorm:"uniqueIndex;not null"`
Password string `json:"-" gorm:"not null"`
FirstName string `json:"first_name" gorm:"not null"`
LastName string `json:"last_name" gorm:"not null"`
Avatar string `json:"avatar"` // Путь к файлу аватара
Phone string `json:"phone"`
Experience string `json:"experience"`
Goals string `json:"goals"`
Newsletter bool `json:"newsletter"`
Role string `json:"role" gorm:"default:user"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
EmailVerified bool `json:"email_verified" gorm:"default:false"`
VerifiedAt time.Time `json:"verified_at"`
// Связи
Workouts []Workout `json:"workouts,omitempty" gorm:"foreignKey:UserID"`
PersonalBests []PersonalBest `json:"personal_bests,omitempty" gorm:"foreignKey:UserID"`
Achievements []Achievement `json:"achievements,omitempty" gorm:"foreignKey:UserID"`
TrainingPlans []TrainingPlan `json:"training_plans,omitempty" gorm:"foreignKey:UserID"`
News []News `json:"news,omitempty" gorm:"foreignKey:AuthorID"`
Comments []Comment `json:"comments,omitempty" gorm:"foreignKey:AuthorID"`
Reviews []Review `json:"reviews,omitempty" gorm:"foreignKey:AuthorID"`
Gallery []Gallery `json:"gallery,omitempty" gorm:"foreignKey:AuthorID"`
EventRegistrations []EventRegistration `json:"event_registrations,omitempty" gorm:"foreignKey:UserID"`
}
type UserUpdate struct {
ID uint `json:"id"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Avatar string `json:"avatar"`
Phone string `json:"phone"`
Experience string `json:"experience"`
Goals string `json:"goals"`
Newsletter bool `json:"newsletter"`
UpdatedAt time.Time `json:"updated_at"`
}
// HashPassword хеширует пароль перед сохранением
func (u *User) HashPassword() error {
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(u.Password), bcrypt.DefaultCost)
if err != nil {
return err
}
u.Password = string(hashedPassword)
return nil
}
// CheckPassword проверяет пароль
func (u *User) CheckPassword(password string) bool {
err := bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password))
return err == nil
}
// BeforeCreate hook для GORM
func (u *User) BeforeCreate(tx *gorm.DB) error {
if u.CreatedAt.IsZero() {
u.CreatedAt = time.Now()
}
if u.UpdatedAt.IsZero() {
u.UpdatedAt = time.Now()
}
return u.HashPassword()
}
// BeforeUpdate hook для GORM
func (u *User) BeforeUpdate(tx *gorm.DB) error {
u.UpdatedAt = time.Now()
return nil
}
// DTO для обновления профиля
type UserUpdateRequest struct {
FirstName string `json:"first_name" validate:"required,min=2,max=100"`
LastName string `json:"last_name" validate:"required,min=2,max=100"`
Phone string `json:"phone" validate:"max=20"`
Experience string `json:"experience" validate:"max=50"`
Goals string `json:"goals" validate:"max=100"`
Newsletter bool `json:"newsletter"`
}
// DTO для ответа с пользователем (без sensitive данных)
type UserResponse struct {
ID uint `json:"id"`
Email string `json:"email"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Avatar string `json:"avatar"`
Phone string `json:"phone"`
Experience string `json:"experience"`
Goals string `json:"goals"`
Newsletter bool `json:"newsletter"`
Role string `json:"role"`
CreatedAt time.Time `json:"created_at"`
}
// DTO для ответа с пользователем и статистикой
type UserWithStatsResponse struct {
UserResponse
Stats *UserStatsResponse `json:"stats,omitempty"`
}
@@ -0,0 +1,61 @@
// models/user_stats.go
package models
import (
"time"
"gorm.io/gorm"
)
type UserStats struct {
ID uint `json:"id" gorm:"primaryKey"`
UserID uint `json:"user_id" gorm:"uniqueIndex;not null"`
TotalDistance float64 `json:"total_distance" gorm:"type:decimal(10,2);default:0"` // Общий пробег в км
TotalTime int `json:"total_time" gorm:"default:0"` // Общее время в минутах
AvgPace string `json:"avg_pace" gorm:"size:20"` // Средний темп
WorkoutsCount int `json:"workouts_count" gorm:"default:0"` // Количество тренировок
CurrentStreak int `json:"current_streak" gorm:"default:0"` // Текущая серия дней подряд
LongestStreak int `json:"longest_streak" gorm:"default:0"` // Самая длинная серия
WeeklyDistance float64 `json:"weekly_distance" gorm:"type:decimal(8,2);default:0"` // Пробег за неделю
MonthlyDistance float64 `json:"monthly_distance" gorm:"type:decimal(8,2);default:0"` // Пробег за месяц
Best5K string `json:"best_5k" gorm:"size:20"` // Лучший результат на 5к
Best10K string `json:"best_10k" gorm:"size:20"` // Лучший результат на 10к
BestHalf string `json:"best_half" gorm:"size:20"` // Лучший результат на полумарафон
BestMarathon string `json:"best_marathon" gorm:"size:20"` // Лучший результат на марафон
LastWorkout time.Time `json:"last_workout"` // Последняя тренировка
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
// Связи
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
}
// BeforeCreate hook
func (us *UserStats) BeforeCreate(tx *gorm.DB) error {
if us.CreatedAt.IsZero() {
us.CreatedAt = time.Now()
}
if us.UpdatedAt.IsZero() {
us.UpdatedAt = time.Now()
}
return nil
}
// BeforeUpdate hook
func (us *UserStats) BeforeUpdate(tx *gorm.DB) error {
us.UpdatedAt = time.Now()
return nil
}
// DTO для статистики пользователя
type UserStatsResponse struct {
TotalDistance float64 `json:"total_distance"`
TotalTime int `json:"total_time"`
AvgPace string `json:"avg_pace"`
WorkoutsCount int `json:"workouts_count"`
CurrentStreak int `json:"current_streak"`
LongestStreak int `json:"longest_streak"`
WeeklyDistance float64 `json:"weekly_distance"`
MonthlyDistance float64 `json:"monthly_distance"`
PersonalBests PersonalBestsSummary `json:"personal_bests"`
}
@@ -0,0 +1,89 @@
// models/workout.go
package models
import (
"time"
"gorm.io/gorm"
)
type WorkoutType string
const (
WorkoutTypeEasy WorkoutType = "easy"
WorkoutTypeTempo WorkoutType = "tempo"
WorkoutTypeInterval WorkoutType = "interval"
WorkoutTypeLong WorkoutType = "long"
WorkoutTypeRecovery WorkoutType = "recovery"
)
type Workout struct {
ID uint `json:"id" gorm:"primaryKey"`
UserID uint `json:"user_id" gorm:"not null;index"`
Type WorkoutType `json:"type" gorm:"type:varchar(20);not null"`
Distance float64 `json:"distance_km" gorm:"type:decimal(5,2);not null"` // Дистанция в км
Duration int `json:"duration_min" gorm:"not null"` // Продолжительность в минутах
Pace string `json:"pace" gorm:"size:20"` // Темп (например, "5:30")
Calories int `json:"calories" gorm:"default:0"` // Сожженные калории
Notes string `json:"notes" gorm:"type:text"` // Заметки к тренировке
Date time.Time `json:"date" gorm:"not null;index"` // Дата тренировки
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
// Связи
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
}
// BeforeCreate hook
func (w *Workout) BeforeCreate(tx *gorm.DB) error {
if w.CreatedAt.IsZero() {
w.CreatedAt = time.Now()
}
if w.UpdatedAt.IsZero() {
w.UpdatedAt = time.Now()
}
return nil
}
// BeforeUpdate hook
func (w *Workout) BeforeUpdate(tx *gorm.DB) error {
w.UpdatedAt = time.Now()
return nil
}
// DTO для создания тренировки
type WorkoutCreateRequest struct {
Type WorkoutType `json:"type" validate:"required,oneof=easy tempo interval long recovery"`
Distance float64 `json:"distance_km" validate:"required,min=0.1,max=1000"`
Duration int `json:"duration_min" validate:"required,min=1,max=1440"`
Pace string `json:"pace" validate:"maxlen=20"`
Calories int `json:"calories" validate:"minint=0,maxint=5000"`
Notes string `json:"notes" validate:"maxlen=1000"`
Date time.Time `json:"date" validate:"required"`
}
// DTO для обновления тренировки
type WorkoutUpdateRequest struct {
Type WorkoutType `json:"type" validate:"oneof=easy tempo interval long recovery"`
Distance float64 `json:"distance_km" validate:"min=0.1,max=1000"`
Duration int `json:"duration_min" validate:"min=1,max=1440"`
Pace string `json:"pace" validate:"maxlen=20"`
Calories int `json:"calories" validate:"minint=0,maxint=5000"`
Notes string `json:"notes" validate:"maxlen=1000"`
Date time.Time `json:"date"`
}
// DTO для статистики тренировок
type WorkoutStatsResponse struct {
TotalWorkouts int `json:"total_workouts"`
TotalDistance float64 `json:"total_distance_km"`
TotalTime int `json:"total_time_min"`
AveragePace string `json:"average_pace"`
MonthlyStats []MonthlyStat `json:"monthly_stats"`
}
type MonthlyStat struct {
Month string `json:"month"`
Distance float64 `json:"distance_km"`
Workouts int `json:"workouts"`
}
@@ -0,0 +1,244 @@
// repositories/achievement_repository.go
package repository
import (
"time"
"gorm.io/gorm"
"api_bb/internal/models"
)
type AchievementRepository interface {
Create(achievement *models.Achievement) error
GetByID(id uint) (*models.Achievement, error)
GetByUserID(userID uint) ([]models.Achievement, error)
GetByUserAndType(userID uint, achievementType models.AchievementType) ([]models.Achievement, error)
GetVerifiedByUserID(userID uint) ([]models.Achievement, error)
GetByDateRange(userID uint, startDate, endDate time.Time) ([]models.Achievement, error)
Update(achievement *models.Achievement) error
Delete(id uint) error
VerifyAchievement(id uint) error
GetUserAchievementsSummary(userID uint) (*models.UserAchievementsResponse, error)
GetRecentAchievements(userID uint, limit int) ([]models.Achievement, error)
CountByType(userID uint) (map[models.AchievementType]int64, error)
ExistsByTitleAndUser(userID uint, title string) (bool, error)
}
type achievementRepository struct {
db *gorm.DB
}
func NewAchievementRepository(db *gorm.DB) AchievementRepository {
return &achievementRepository{db: db}
}
// Create создает новое достижение
func (r *achievementRepository) Create(achievement *models.Achievement) error {
return r.db.Create(achievement).Error
}
// GetByID возвращает достижение по ID
func (r *achievementRepository) GetByID(id uint) (*models.Achievement, error) {
var achievement models.Achievement
err := r.db.Preload("User").First(&achievement, id).Error
if err != nil {
return nil, err
}
return &achievement, nil
}
// GetByUserID возвращает все достижения пользователя
func (r *achievementRepository) GetByUserID(userID uint) ([]models.Achievement, error) {
var achievements []models.Achievement
err := r.db.Where("user_id = ?", userID).
Order("date DESC, created_at DESC").
Find(&achievements).Error
if err != nil {
return nil, err
}
return achievements, nil
}
// GetByUserAndType возвращает достижения пользователя по типу
func (r *achievementRepository) GetByUserAndType(userID uint, achievementType models.AchievementType) ([]models.Achievement, error) {
var achievements []models.Achievement
err := r.db.Where("user_id = ? AND type = ?", userID, achievementType).
Order("date DESC").
Find(&achievements).Error
if err != nil {
return nil, err
}
return achievements, nil
}
// GetVerifiedByUserID возвращает подтвержденные достижения пользователя
func (r *achievementRepository) GetVerifiedByUserID(userID uint) ([]models.Achievement, error) {
var achievements []models.Achievement
err := r.db.Where("user_id = ? AND verified = ?", userID, true).
Order("date DESC").
Find(&achievements).Error
if err != nil {
return nil, err
}
return achievements, nil
}
// GetByDateRange возвращает достижения за период времени
func (r *achievementRepository) GetByDateRange(userID uint, startDate, endDate time.Time) ([]models.Achievement, error) {
var achievements []models.Achievement
err := r.db.Where("user_id = ? AND date BETWEEN ? AND ?", userID, startDate, endDate).
Order("date DESC").
Find(&achievements).Error
if err != nil {
return nil, err
}
return achievements, nil
}
// Update обновляет достижение
func (r *achievementRepository) Update(achievement *models.Achievement) error {
return r.db.Save(achievement).Error
}
// Delete удаляет достижение
func (r *achievementRepository) Delete(id uint) error {
return r.db.Delete(&models.Achievement{}, id).Error
}
// VerifyAchievement подтверждает достижение
func (r *achievementRepository) VerifyAchievement(id uint) error {
return r.db.Model(&models.Achievement{}).
Where("id = ?", id).
Update("verified", true).
Error
}
// GetUserAchievementsSummary возвращает сводку по достижениям пользователя
func (r *achievementRepository) GetUserAchievementsSummary(userID uint) (*models.UserAchievementsResponse, error) {
var totalCount int64
var verifiedCount int64
// Считаем общее количество достижений
err := r.db.Model(&models.Achievement{}).
Where("user_id = ?", userID).
Count(&totalCount).Error
if err != nil {
return nil, err
}
// Считаем количество подтвержденных достижений
err = r.db.Model(&models.Achievement{}).
Where("user_id = ? AND verified = ?", userID, true).
Count(&verifiedCount).Error
if err != nil {
return nil, err
}
// Получаем все достижения пользователя
achievements, err := r.GetByUserID(userID)
if err != nil {
return nil, err
}
// Вычисляем процент прогресса
progressPercent := 0.0
if totalCount > 0 {
progressPercent = (float64(verifiedCount) / float64(totalCount)) * 100
}
return &models.UserAchievementsResponse{
TotalAchievements: int(totalCount),
Completed: int(verifiedCount),
ProgressPercent: progressPercent,
Achievements: achievements,
}, nil
}
// GetRecentAchievements возвращает последние достижения пользователя
func (r *achievementRepository) GetRecentAchievements(userID uint, limit int) ([]models.Achievement, error) {
var achievements []models.Achievement
err := r.db.Where("user_id = ?", userID).
Order("created_at DESC").
Limit(limit).
Find(&achievements).Error
if err != nil {
return nil, err
}
return achievements, nil
}
// CountByType возвращает количество достижений по типам
func (r *achievementRepository) CountByType(userID uint) (map[models.AchievementType]int64, error) {
type CountResult struct {
Type models.AchievementType
Count int64
}
var results []CountResult
err := r.db.Model(&models.Achievement{}).
Select("type, COUNT(*) as count").
Where("user_id = ?", userID).
Group("type").
Scan(&results).Error
if err != nil {
return nil, err
}
counts := make(map[models.AchievementType]int64)
for _, result := range results {
counts[result.Type] = result.Count
}
return counts, nil
}
// ExistsByTitleAndUser проверяет, существует ли достижение с таким названием у пользователя
func (r *achievementRepository) ExistsByTitleAndUser(userID uint, title string) (bool, error) {
var count int64
err := r.db.Model(&models.Achievement{}).
Where("user_id = ? AND title = ?", userID, title).
Count(&count).Error
if err != nil {
return false, err
}
return count > 0, nil
}
// GetUnverifiedAchievements возвращает неподтвержденные достижения
func (r *achievementRepository) GetUnverifiedAchievements(userID uint) ([]models.Achievement, error) {
var achievements []models.Achievement
err := r.db.Where("user_id = ? AND verified = ?", userID, false).
Order("created_at DESC").
Find(&achievements).Error
if err != nil {
return nil, err
}
return achievements, nil
}
// GetAchievementsWithPagination возвращает достижения с пагинацией
func (r *achievementRepository) GetAchievementsWithPagination(userID uint, page, pageSize int) ([]models.Achievement, int64, error) {
var achievements []models.Achievement
var totalCount int64
// Считаем общее количество
err := r.db.Model(&models.Achievement{}).
Where("user_id = ?", userID).
Count(&totalCount).Error
if err != nil {
return nil, 0, err
}
// Получаем данные с пагинацией
offset := (page - 1) * pageSize
err = r.db.Where("user_id = ?", userID).
Order("date DESC, created_at DESC").
Offset(offset).
Limit(pageSize).
Find(&achievements).Error
if err != nil {
return nil, 0, err
}
return achievements, totalCount, nil
}
@@ -0,0 +1,42 @@
package repository
import (
"api_bb/internal/models"
"gorm.io/gorm"
)
type CommentRepository interface {
Create(comment *models.Comment) error
GetByNewsID(newsID uint) ([]models.Comment, error)
Delete(id uint) error
GetByID(id uint) (*models.Comment, error)
}
type commentRepository struct {
db *gorm.DB
}
func NewCommentRepository(db *gorm.DB) CommentRepository {
return &commentRepository{db: db}
}
func (r *commentRepository) Create(comment *models.Comment) error {
return r.db.Create(comment).Error
}
func (r *commentRepository) GetByNewsID(newsID uint) ([]models.Comment, error) {
var comments []models.Comment
err := r.db.Preload("Author").Where("news_id = ?", newsID).
Order("created_at ASC").Find(&comments).Error
return comments, err
}
func (r *commentRepository) Delete(id uint) error {
return r.db.Delete(&models.Comment{}, id).Error
}
func (r *commentRepository) GetByID(id uint) (*models.Comment, error) {
var comment models.Comment
err := r.db.Preload("Author").Where("id = ?", id).First(&comment).Error
return &comment, err
}
@@ -0,0 +1,83 @@
// repository/email_repository.go
package repository
import (
"time"
"api_bb/internal/models"
"gorm.io/gorm"
)
type EmailRepository struct {
db *gorm.DB
}
func NewEmailRepository(db *gorm.DB) *EmailRepository {
return &EmailRepository{db: db}
}
func (r *EmailRepository) CreateVerificationToken(verification *models.EmailVerification) error {
return r.db.Create(verification).Error
}
func (r *EmailRepository) GetVerificationToken(token string) (*models.EmailVerification, error) {
var verification models.EmailVerification
err := r.db.Where("token = ? AND used = ? AND expires_at > ?", token, false, time.Now()).
Preload("User").
First(&verification).Error
if err != nil {
return nil, err
}
return &verification, nil
}
func (r *EmailRepository) MarkTokenAsUsed(token string) error {
return r.db.Model(&models.EmailVerification{}).
Where("token = ?", token).
Updates(map[string]interface{}{
"used": true,
"updated_at": time.Now(),
}).Error
}
func (r *EmailRepository) DeleteExpiredTokens() error {
return r.db.Where("expires_at < ?", time.Now()).Delete(&models.EmailVerification{}).Error
}
func (r *EmailRepository) GetUsersWithNewsletter() ([]models.User, error) {
var users []models.User
err := r.db.Where("newsletter = ? AND email_verified = ?", true, true).
Find(&users).Error
return users, err
}
// MarkEmailAsVerified помечает email пользователя как верифицированный
func (r *EmailRepository) MarkEmailAsVerified(userID uint) error {
return r.db.Model(&models.User{}).
Where("id = ?", userID).
Updates(map[string]interface{}{
"email_verified": true,
"updated_at": time.Now(),
}).Error
}
// GetUserByEmail возвращает пользователя по email
func (r *EmailRepository) GetUserByEmail(email string) (*models.User, error) {
var user models.User
err := r.db.Where("email = ?", email).First(&user).Error
if err != nil {
return nil, err
}
return &user, nil
}
// UpdatePassword обновляет пароль пользователя
func (r *EmailRepository) UpdatePassword(userID uint, newPassword string) error {
return r.db.Model(&models.User{}).
Where("id = ?", userID).
Updates(map[string]interface{}{
"password": newPassword,
"updated_at": time.Now(),
}).Error
}
@@ -0,0 +1,94 @@
// repository/event_registration_repository.go
package repository
import (
"api_bb/internal/models"
"fmt"
"gorm.io/gorm"
)
type EventRegistrationRepository interface {
Create(registration *models.EventRegistration) error
FindByID(id uint) (*models.EventRegistration, error)
FindByEventID(eventID uint) ([]models.EventRegistration, error)
FindByUserID(userID uint) ([]models.EventRegistration, error)
FindByEventAndUser(eventID, userID uint) (*models.EventRegistration, error)
Update(registration *models.EventRegistration) error
Delete(id uint) error
UpdateStatus(registrationID uint, status string) error
UpdateResultTime(registrationID uint, resultTime string) error
CountByEventID(eventID uint) (int64, error)
}
type eventRegistrationRepository struct {
db *gorm.DB
}
func NewEventRegistrationRepository(db *gorm.DB) EventRegistrationRepository {
return &eventRegistrationRepository{db: db}
}
func (r *eventRegistrationRepository) Create(registration *models.EventRegistration) error {
return r.db.Create(registration).Error
}
func (r *eventRegistrationRepository) FindByID(id uint) (*models.EventRegistration, error) {
var registration models.EventRegistration
err := r.db.Preload("Event").Preload("User").First(&registration, id).Error
return &registration, err
}
func (r *eventRegistrationRepository) FindByEventID(eventID uint) ([]models.EventRegistration, error) {
var registrations []models.EventRegistration
err := r.db.Preload("User").Where("event_id = ?", eventID).Find(&registrations).Error
return registrations, err
}
func (r *eventRegistrationRepository) FindByUserID(userID uint) ([]models.EventRegistration, error) {
var registrations []models.EventRegistration
err := r.db.Preload("Event").Where("user_id = ?", userID).Find(&registrations).Error
return registrations, err
}
func (r *eventRegistrationRepository) FindByEventAndUser(eventID, userID uint) (*models.EventRegistration, error) {
var registration models.EventRegistration
err := r.db.Where("event_id = ? AND user_id = ?", eventID, userID).First(&registration).Error
return &registration, err
}
func (r *eventRegistrationRepository) Update(registration *models.EventRegistration) error {
return r.db.Save(registration).Error
}
func (r *eventRegistrationRepository) Delete(id uint) error {
return r.db.Delete(&models.EventRegistration{}, id).Error
}
func (r *eventRegistrationRepository) UpdateStatus(registrationID uint, status string) error {
result := r.db.Model(&models.EventRegistration{}).Where("id = ?", registrationID).Update("status", status)
if result.Error != nil {
return result.Error
}
if result.RowsAffected == 0 {
return fmt.Errorf("registration not found")
}
return nil
}
func (r *eventRegistrationRepository) UpdateResultTime(registrationID uint, resultTime string) error {
result := r.db.Model(&models.EventRegistration{}).Where("id = ?", registrationID).Update("result_time", resultTime)
if result.Error != nil {
return result.Error
}
if result.RowsAffected == 0 {
return fmt.Errorf("registration not found")
}
return nil
}
func (r *eventRegistrationRepository) CountByEventID(eventID uint) (int64, error) {
var count int64
err := r.db.Model(&models.EventRegistration{}).Where("event_id = ? AND status IN ?", eventID, []string{"pending", "confirmed"}).Count(&count).Error
return count, err
}
@@ -0,0 +1,95 @@
// repository/event_repository.go
package repository
import (
"api_bb/internal/models"
"fmt"
"time"
"gorm.io/gorm"
)
type EventRepository interface {
Create(event *models.Event) error
FindByID(id uint) (*models.Event, error)
FindAll() ([]models.Event, error)
Update(event *models.Event) error
Delete(id uint) error
FindByType(eventType models.EventType) ([]models.Event, error)
FindUpcoming() ([]models.Event, error)
FindByDateRange(startDate, endDate time.Time) ([]models.Event, error)
UpdateParticipantsCount(eventID uint, count int) error
UpdateRegistrationStatus(eventID uint, registrationOpen bool) error
}
type eventRepository struct {
db *gorm.DB
}
func NewEventRepository(db *gorm.DB) EventRepository {
return &eventRepository{db: db}
}
func (r *eventRepository) Create(event *models.Event) error {
return r.db.Create(event).Error
}
func (r *eventRepository) FindByID(id uint) (*models.Event, error) {
var event models.Event
err := r.db.Preload("Registrations").First(&event, id).Error
return &event, err
}
func (r *eventRepository) FindAll() ([]models.Event, error) {
var events []models.Event
err := r.db.Order("date DESC").Find(&events).Error
return events, err
}
func (r *eventRepository) Update(event *models.Event) error {
return r.db.Save(event).Error
}
func (r *eventRepository) Delete(id uint) error {
return r.db.Delete(&models.Event{}, id).Error
}
func (r *eventRepository) FindByType(eventType models.EventType) ([]models.Event, error) {
var events []models.Event
err := r.db.Where("type = ?", eventType).Order("date DESC").Find(&events).Error
return events, err
}
func (r *eventRepository) FindUpcoming() ([]models.Event, error) {
var events []models.Event
err := r.db.Where("date >= ?", time.Now()).Order("date ASC").Find(&events).Error
return events, err
}
func (r *eventRepository) FindByDateRange(startDate, endDate time.Time) ([]models.Event, error) {
var events []models.Event
err := r.db.Where("date BETWEEN ? AND ?", startDate, endDate).Order("date ASC").Find(&events).Error
return events, err
}
func (r *eventRepository) UpdateParticipantsCount(eventID uint, count int) error {
result := r.db.Model(&models.Event{}).Where("id = ?", eventID).Update("participants_count", count)
if result.Error != nil {
return result.Error
}
if result.RowsAffected == 0 {
return fmt.Errorf("event not found")
}
return nil
}
func (r *eventRepository) UpdateRegistrationStatus(eventID uint, registrationOpen bool) error {
result := r.db.Model(&models.Event{}).Where("id = ?", eventID).Update("registration_open", registrationOpen)
if result.Error != nil {
return result.Error
}
if result.RowsAffected == 0 {
return fmt.Errorf("event not found")
}
return nil
}
@@ -0,0 +1,124 @@
// repository/gallery_repository.go
package repository
import (
"api_bb/internal/models"
"fmt"
"time"
"gorm.io/gorm"
)
type GalleryRepository interface {
Create(gallery *models.Gallery) error
FindByID(id uint) (*models.Gallery, error)
FindAll() ([]models.Gallery, error)
Update(gallery *models.Gallery) error
Delete(id uint) error
FindByCategory(category models.GalleryCategory) ([]models.Gallery, error)
FindByAuthor(authorID uint) ([]models.Gallery, error)
FindPopular(limit int) ([]models.Gallery, error)
FindRecent(limit int) ([]models.Gallery, error)
IncrementViews(galleryID uint) error
IncrementLikes(galleryID uint) error
DecrementLikes(galleryID uint) error
FindByEventDateRange(startDate, endDate time.Time) ([]models.Gallery, error)
}
type galleryRepository struct {
db *gorm.DB
}
func NewGalleryRepository(db *gorm.DB) GalleryRepository {
return &galleryRepository{db: db}
}
func (r *galleryRepository) Create(gallery *models.Gallery) error {
return r.db.Create(gallery).Error
}
func (r *galleryRepository) FindByID(id uint) (*models.Gallery, error) {
var gallery models.Gallery
err := r.db.Preload("Author").First(&gallery, id).Error
return &gallery, err
}
func (r *galleryRepository) FindAll() ([]models.Gallery, error) {
var galleries []models.Gallery
err := r.db.Preload("Author").Order("created_at DESC").Find(&galleries).Error
return galleries, err
}
func (r *galleryRepository) Update(gallery *models.Gallery) error {
return r.db.Save(gallery).Error
}
func (r *galleryRepository) Delete(id uint) error {
return r.db.Delete(&models.Gallery{}, id).Error
}
func (r *galleryRepository) FindByCategory(category models.GalleryCategory) ([]models.Gallery, error) {
var galleries []models.Gallery
err := r.db.Preload("Author").Where("category = ?", category).Order("created_at DESC").Find(&galleries).Error
return galleries, err
}
func (r *galleryRepository) FindByAuthor(authorID uint) ([]models.Gallery, error) {
var galleries []models.Gallery
err := r.db.Preload("Author").Where("author_id = ?", authorID).Order("created_at DESC").Find(&galleries).Error
return galleries, err
}
func (r *galleryRepository) FindPopular(limit int) ([]models.Gallery, error) {
var galleries []models.Gallery
err := r.db.Preload("Author").Order("views DESC, likes DESC").Limit(limit).Find(&galleries).Error
return galleries, err
}
func (r *galleryRepository) FindRecent(limit int) ([]models.Gallery, error) {
var galleries []models.Gallery
err := r.db.Preload("Author").Order("created_at DESC").Limit(limit).Find(&galleries).Error
return galleries, err
}
func (r *galleryRepository) IncrementViews(galleryID uint) error {
result := r.db.Model(&models.Gallery{}).Where("id = ?", galleryID).Update("views", gorm.Expr("views + ?", 1))
if result.Error != nil {
return result.Error
}
if result.RowsAffected == 0 {
return fmt.Errorf("gallery not found")
}
return nil
}
func (r *galleryRepository) IncrementLikes(galleryID uint) error {
result := r.db.Model(&models.Gallery{}).Where("id = ?", galleryID).Update("likes", gorm.Expr("likes + ?", 1))
if result.Error != nil {
return result.Error
}
if result.RowsAffected == 0 {
return fmt.Errorf("gallery not found")
}
return nil
}
func (r *galleryRepository) DecrementLikes(galleryID uint) error {
result := r.db.Model(&models.Gallery{}).Where("id = ? AND likes > 0", galleryID).Update("likes", gorm.Expr("likes - ?", 1))
if result.Error != nil {
return result.Error
}
if result.RowsAffected == 0 {
return fmt.Errorf("gallery not found or likes already zero")
}
return nil
}
func (r *galleryRepository) FindByEventDateRange(startDate, endDate time.Time) ([]models.Gallery, error) {
var galleries []models.Gallery
err := r.db.Preload("Author").
Where("event_date BETWEEN ? AND ?", startDate, endDate).
Order("event_date DESC").
Find(&galleries).Error
return galleries, err
}
@@ -0,0 +1,88 @@
package repository
import (
"api_bb/internal/models"
"gorm.io/gorm"
)
type NewsRepository interface {
Create(news *models.News) error
GetByID(id uint) (*models.News, error)
GetAll(limit, offset int, category string) ([]models.News, int64, error)
Update(news *models.News) error
Delete(id uint) error
IncrementViews(id uint) error
GetByAuthor(authorID uint, limit, offset int) ([]models.News, int64, error)
}
type newsRepository struct {
db *gorm.DB
}
func NewNewsRepository(db *gorm.DB) NewsRepository {
return &newsRepository{db: db}
}
func (r *newsRepository) Create(news *models.News) error {
return r.db.Create(news).Error
}
func (r *newsRepository) GetByID(id uint) (*models.News, error) {
var news models.News
err := r.db.Preload("Author").Preload("Comments.Author").
Where("id = ?", id).First(&news).Error
return &news, err
}
func (r *newsRepository) GetAll(limit, offset int, category string) ([]models.News, int64, error) {
var news []models.News
var total int64
query := r.db.Preload("Author")
if category != "" && category != "all" {
query = query.Where("category = ?", category)
}
// Получаем общее количество
if err := query.Model(&models.News{}).Count(&total).Error; err != nil {
return nil, 0, err
}
// Получаем данные с пагинацией
err := query.Order("created_at DESC").
Limit(limit).Offset(offset).
Find(&news).Error
return news, total, err
}
func (r *newsRepository) Update(news *models.News) error {
return r.db.Save(news).Error
}
func (r *newsRepository) Delete(id uint) error {
return r.db.Delete(&models.News{}, id).Error
}
func (r *newsRepository) IncrementViews(id uint) error {
return r.db.Model(&models.News{}).Where("id = ?", id).
Update("views", gorm.Expr("views + ?", 1)).Error
}
func (r *newsRepository) GetByAuthor(authorID uint, limit, offset int) ([]models.News, int64, error) {
var news []models.News
var total int64
query := r.db.Preload("Author").Where("author_id = ?", authorID)
if err := query.Model(&models.News{}).Count(&total).Error; err != nil {
return nil, 0, err
}
err := query.Order("created_at DESC").
Limit(limit).Offset(offset).
Find(&news).Error
return news, total, err
}
@@ -0,0 +1,239 @@
// repositories/personal_best_repository.go
package repository
import (
"time"
"api_bb/internal/models"
"api_bb/pkg/utils"
"gorm.io/gorm"
)
type PersonalBestRepository interface {
Create(personalBest *models.PersonalBest) error
GetByID(id uint) (*models.PersonalBest, error)
GetByUserID(userID uint) ([]models.PersonalBest, error)
GetByUserAndDistance(userID uint, distanceType models.DistanceType) ([]models.PersonalBest, error)
GetBestByDistance(userID uint, distanceType models.DistanceType) (*models.PersonalBest, error)
Update(personalBest *models.PersonalBest) error
Delete(id uint) error
GetVerifiedByUserID(userID uint) ([]models.PersonalBest, error)
GetByDateRange(userID uint, startDate, endDate time.Time) ([]models.PersonalBest, error)
GetPersonalBestsSummary(userID uint) (*models.PersonalBestsSummary, error)
ExistsBetterTime(userID uint, distanceType models.DistanceType, time string) (bool, error)
CalculatePace(timeStr string, distanceType models.DistanceType) (string, error)
GetRecentPersonalBests(userID uint, limit int) ([]models.PersonalBest, error)
GetByEventName(userID uint, eventName string) ([]models.PersonalBest, error)
}
type personalBestRepository struct {
db *gorm.DB
}
func NewPersonalBestRepository(db *gorm.DB) PersonalBestRepository {
return &personalBestRepository{db: db}
}
// Create создает новый личный рекорд
func (r *personalBestRepository) Create(personalBest *models.PersonalBest) error {
return r.db.Create(personalBest).Error
}
// GetByID возвращает личный рекорд по ID
func (r *personalBestRepository) GetByID(id uint) (*models.PersonalBest, error) {
var personalBest models.PersonalBest
err := r.db.Preload("User").First(&personalBest, id).Error
if err != nil {
return nil, err
}
return &personalBest, nil
}
// GetByUserID возвращает все личные рекорды пользователя
func (r *personalBestRepository) GetByUserID(userID uint) ([]models.PersonalBest, error) {
var personalBests []models.PersonalBest
err := r.db.Where("user_id = ?", userID).
Preload("User").
Order("distance_type, time").
Find(&personalBests).Error
if err != nil {
return nil, err
}
return personalBests, nil
}
// GetByUserAndDistance возвращает личные рекорды пользователя по дистанции
func (r *personalBestRepository) GetByUserAndDistance(userID uint, distanceType models.DistanceType) ([]models.PersonalBest, error) {
var personalBests []models.PersonalBest
err := r.db.Where("user_id = ? AND distance_type = ?", userID, distanceType).
Preload("User").
Order("time").
Find(&personalBests).Error
if err != nil {
return nil, err
}
return personalBests, nil
}
// GetBestByDistance возвращает лучший результат пользователя на дистанции
func (r *personalBestRepository) GetBestByDistance(userID uint, distanceType models.DistanceType) (*models.PersonalBest, error) {
var personalBest models.PersonalBest
err := r.db.Where("user_id = ? AND distance_type = ?", userID, distanceType).
Preload("User").
Order("time").
First(&personalBest).Error
if err != nil {
return nil, err
}
return &personalBest, nil
}
// Update обновляет личный рекорд
func (r *personalBestRepository) Update(personalBest *models.PersonalBest) error {
return r.db.Save(personalBest).Error
}
// Delete удаляет личный рекорд
func (r *personalBestRepository) Delete(id uint) error {
return r.db.Delete(&models.PersonalBest{}, id).Error
}
// GetVerifiedByUserID возвращает подтвержденные личные рекорды пользователя
func (r *personalBestRepository) GetVerifiedByUserID(userID uint) ([]models.PersonalBest, error) {
var personalBests []models.PersonalBest
err := r.db.Where("user_id = ? AND verified = ?", userID, true).
Preload("User").
Order("distance_type, time").
Find(&personalBests).Error
if err != nil {
return nil, err
}
return personalBests, nil
}
// GetByDateRange возвращает личные рекорды за период времени
func (r *personalBestRepository) GetByDateRange(userID uint, startDate, endDate time.Time) ([]models.PersonalBest, error) {
var personalBests []models.PersonalBest
err := r.db.Where("user_id = ? AND date BETWEEN ? AND ?", userID, startDate, endDate).
Preload("User").
Order("date DESC, distance_type").
Find(&personalBests).Error
if err != nil {
return nil, err
}
return personalBests, nil
}
// GetRecentPersonalBests возвращает последние личные рекорды
func (r *personalBestRepository) GetRecentPersonalBests(userID uint, limit int) ([]models.PersonalBest, error) {
var personalBests []models.PersonalBest
err := r.db.Where("user_id = ?", userID).
Preload("User").
Order("created_at DESC").
Limit(limit).
Find(&personalBests).Error
if err != nil {
return nil, err
}
return personalBests, nil
}
// GetByEventName возвращает личные рекорды по названию события
func (r *personalBestRepository) GetByEventName(userID uint, eventName string) ([]models.PersonalBest, error) {
var personalBests []models.PersonalBest
err := r.db.Where("user_id = ? AND event_name LIKE ?", userID, "%"+eventName+"%").
Preload("User").
Order("date DESC").
Find(&personalBests).Error
if err != nil {
return nil, err
}
return personalBests, nil
}
// GetPersonalBestsSummary возвращает сводку лучших результатов по дистанциям
// GetPersonalBestsSummary возвращает сводку лучших результатов по дистанциям
func (r *personalBestRepository) GetPersonalBestsSummary(userID uint) (*models.PersonalBestsSummary, error) {
summary := &models.PersonalBestsSummary{}
// Получаем лучший результат для каждой дистанции
distances := []models.DistanceType{
models.Distance5K,
models.Distance10K,
models.DistanceHalf,
models.DistanceFull,
}
for _, distance := range distances {
best, err := r.GetBestByDistance(userID, distance)
if err != nil && err != gorm.ErrRecordNotFound {
return nil, err
}
if best != nil {
switch distance {
case models.Distance5K:
summary.Best5K = best.Time
summary.Best5KPace = best.Pace
case models.Distance10K:
summary.Best10K = best.Time
summary.Best10KPace = best.Pace
case models.DistanceHalf:
summary.BestHalf = best.Time
summary.BestHalfPace = best.Pace
case models.DistanceFull:
summary.BestMarathon = best.Time
summary.BestMarathonPace = best.Pace
}
}
}
return summary, nil
}
// ExistsBetterTime проверяет, есть ли у пользователя уже лучший результат на этой дистанции
func (r *personalBestRepository) ExistsBetterTime(userID uint, distanceType models.DistanceType, time string) (bool, error) {
var count int64
err := r.db.Model(&models.PersonalBest{}).
Where("user_id = ? AND distance_type = ? AND time < ?", userID, distanceType, time).
Count(&count).Error
if err != nil {
return false, err
}
return count > 0, nil
}
// CalculatePace вычисляет темп на основе времени и дистанции
func (r *personalBestRepository) CalculatePace(timeStr string, distanceType models.DistanceType) (string, error) {
// Парсим время из строки "HH:MM:SS"
t, err := time.Parse("15:04:05", timeStr)
if err != nil {
return "", err
}
// Преобразуем в секунды
totalSeconds := t.Hour()*3600 + t.Minute()*60 + t.Second()
// Определяем дистанцию в метрах
var distanceMeters float64
switch distanceType {
case models.Distance5K:
distanceMeters = 5000
case models.Distance10K:
distanceMeters = 10000
case models.DistanceHalf:
distanceMeters = 21097.5 // 21.0975 km
case models.DistanceFull:
distanceMeters = 42195 // 42.195 km
default:
return "", nil // Для других дистанций не вычисляем темп
}
// Вычисляем темп в секундах на километр
paceSecondsPerKm := float64(totalSeconds) / (distanceMeters / 1000)
// Форматируем темп в "MM:SS"
minutes := int(paceSecondsPerKm) / 60
seconds := int(paceSecondsPerKm) % 60
return utils.FormatPace(minutes, seconds), nil
}
@@ -0,0 +1,164 @@
// repository/review_repository.go
package repository
import (
"api_bb/internal/models"
"database/sql"
"math"
"gorm.io/gorm"
)
type ReviewRepository interface {
Create(review *models.Review) error
GetByID(id uint) (*models.Review, error)
GetAll(page, limit int, sortBy, filter string) ([]models.Review, int64, error)
GetByAuthorID(authorID uint) ([]models.Review, error)
Update(review *models.Review) error
Delete(id uint) error
GetStats() (*models.ReviewsStatsResponse, error)
GetRatingDistribution() (map[int]int, error)
}
type reviewRepository struct {
db *gorm.DB
}
func NewReviewRepository(db *gorm.DB) ReviewRepository {
return &reviewRepository{db: db}
}
func (r *reviewRepository) Create(review *models.Review) error {
return r.db.Create(review).Error
}
func (r *reviewRepository) GetByID(id uint) (*models.Review, error) {
var review models.Review
err := r.db.Preload("Author").First(&review, id).Error
return &review, err
}
func (r *reviewRepository) GetAll(page, limit int, sortBy, filter string) ([]models.Review, int64, error) {
var reviews []models.Review
var total int64
query := r.db.Model(&models.Review{}).Preload("Author")
// Применяем фильтрацию по рейтингу
if filter != "" && filter != "all" {
query = query.Where("rating >= ?", filter)
}
// Считаем общее количество
if err := query.Count(&total).Error; err != nil {
return nil, 0, err
}
// Применяем сортировку
switch sortBy {
case "newest":
query = query.Order("created_at DESC")
case "oldest":
query = query.Order("created_at ASC")
case "highest":
query = query.Order("rating DESC, created_at DESC")
case "lowest":
query = query.Order("rating ASC, created_at DESC")
default:
query = query.Order("created_at DESC")
}
// Применяем пагинацию
offset := (page - 1) * limit
err := query.Offset(offset).Limit(limit).Find(&reviews).Error
return reviews, total, err
}
func (r *reviewRepository) GetByAuthorID(authorID uint) ([]models.Review, error) {
var reviews []models.Review
err := r.db.Where("author_id = ?", authorID).Preload("Author").Find(&reviews).Error
return reviews, err
}
func (r *reviewRepository) Update(review *models.Review) error {
return r.db.Save(review).Error
}
func (r *reviewRepository) Delete(id uint) error {
return r.db.Delete(&models.Review{}, id).Error
}
func (r *reviewRepository) GetStats() (*models.ReviewsStatsResponse, error) {
var totalReviews int64
var averageRating float64
var successStories int64
// Общее количество отзывов
if err := r.db.Model(&models.Review{}).Count(&totalReviews).Error; err != nil {
return nil, err
}
// Средний рейтинг - ИСПРАВЛЕННАЯ ЧАСТЬ
var nullRating sql.NullFloat64
if err := r.db.Model(&models.Review{}).Select("AVG(rating)").Row().Scan(&nullRating); err != nil {
return nil, err
}
if nullRating.Valid {
averageRating = nullRating.Float64
} else {
averageRating = 0
}
// Количество успешных историй (отзывы с рейтингом >= 4 и достижениями)
if err := r.db.Model(&models.Review{}).
Where("rating >= ? AND achievement != ?", 4, "").
Count(&successStories).Error; err != nil {
return nil, err
}
// Распределение по рейтингам
ratingDistribution, err := r.GetRatingDistribution()
if err != nil {
return nil, err
}
return &models.ReviewsStatsResponse{
TotalReviews: int(totalReviews),
AverageRating: math.Round(averageRating*100) / 100, // Округляем до 2 знаков
SuccessStories: int(successStories),
RatingDistribution: ratingDistribution,
}, nil
}
func (r *reviewRepository) GetRatingDistribution() (map[int]int, error) {
var results []struct {
Rating int
Count int
}
err := r.db.Model(&models.Review{}).
Select("rating, COUNT(*) as count").
Group("rating").
Order("rating DESC").
Scan(&results).Error
if err != nil {
return nil, err
}
distribution := make(map[int]int)
for _, result := range results {
distribution[result.Rating] = result.Count
}
// Заполняем отсутствующие рейтинги нулями
for i := 1; i <= 5; i++ {
if _, exists := distribution[i]; !exists {
distribution[i] = 0
}
}
return distribution, nil
}
@@ -0,0 +1,136 @@
// repositories/training_plan_repository.go
package repository
import (
"api_bb/internal/models"
"time"
"gorm.io/gorm"
)
type TrainingPlanRepository struct {
db *gorm.DB
}
func NewTrainingPlanRepository(db *gorm.DB) *TrainingPlanRepository {
return &TrainingPlanRepository{db: db}
}
// Create создает новый план тренировок
func (r *TrainingPlanRepository) Create(plan *models.TrainingPlan) error {
return r.db.Create(plan).Error
}
// GetByID возвращает план тренировок по ID
func (r *TrainingPlanRepository) GetByID(id uint) (*models.TrainingPlan, error) {
var plan models.TrainingPlan
err := r.db.Preload("Workouts").First(&plan, id).Error
if err != nil {
return nil, err
}
return &plan, nil
}
// GetByUserID возвращает все планы тренировок пользователя
func (r *TrainingPlanRepository) GetByUserID(userID uint) ([]models.TrainingPlan, error) {
var plans []models.TrainingPlan
err := r.db.Preload("Workouts").Where("user_id = ?", userID).Find(&plans).Error
if err != nil {
return nil, err
}
return plans, nil
}
// Update обновляет план тренировок
func (r *TrainingPlanRepository) Update(plan *models.TrainingPlan) error {
return r.db.Save(plan).Error
}
// Delete удаляет план тренировок
func (r *TrainingPlanRepository) Delete(id uint) error {
return r.db.Delete(&models.TrainingPlan{}, id).Error
}
// UpdateCurrentWeek обновляет текущую неделю плана тренировок
func (r *TrainingPlanRepository) UpdateCurrentWeek(id uint, currentWeek int) error {
return r.db.Model(&models.TrainingPlan{}).Where("id = ?", id).Update("current_week", currentWeek).Error
}
// MarkAsCompleted помечает план тренировок как завершенный
func (r *TrainingPlanRepository) MarkAsCompleted(id uint) error {
return r.db.Model(&models.TrainingPlan{}).Where("id = ?", id).Update("completed", true).Error
}
// GetActivePlan возвращает активный (не завершенный) план тренировок пользователя
func (r *TrainingPlanRepository) GetActivePlan(userID uint) (*models.TrainingPlan, error) {
var plan models.TrainingPlan
err := r.db.Preload("Workouts").Where("user_id = ? AND completed = ?", userID, false).First(&plan).Error
if err != nil {
return nil, err
}
return &plan, nil
}
// CreateWorkout создает тренировку в плане
func (r *TrainingPlanRepository) CreateWorkout(workout *models.TrainingWorkout) error {
return r.db.Create(workout).Error
}
// GetWorkoutByID возвращает тренировку по ID
func (r *TrainingPlanRepository) GetWorkoutByID(id uint) (*models.TrainingWorkout, error) {
var workout models.TrainingWorkout
err := r.db.First(&workout, id).Error
if err != nil {
return nil, err
}
return &workout, nil
}
// GetWorkoutsByPlanID возвращает все тренировки плана
func (r *TrainingPlanRepository) GetWorkoutsByPlanID(planID uint) ([]models.TrainingWorkout, error) {
var workouts []models.TrainingWorkout
err := r.db.Where("plan_id = ?", planID).Find(&workouts).Error
if err != nil {
return nil, err
}
return workouts, nil
}
// UpdateWorkout обновляет тренировку
func (r *TrainingPlanRepository) UpdateWorkout(workout *models.TrainingWorkout) error {
return r.db.Save(workout).Error
}
// DeleteWorkout удаляет тренировку
func (r *TrainingPlanRepository) DeleteWorkout(id uint) error {
return r.db.Delete(&models.TrainingWorkout{}, id).Error
}
// MarkWorkoutAsCompleted помечает тренировку как завершенную
func (r *TrainingPlanRepository) MarkWorkoutAsCompleted(id uint) error {
now := time.Now()
return r.db.Model(&models.TrainingWorkout{}).Where("id = ?", id).Updates(map[string]interface{}{
"completed": true,
"completed_at": now,
}).Error
}
// GetWorkoutsByWeek возвращает тренировки для определенной недели плана
func (r *TrainingPlanRepository) GetWorkoutsByWeek(planID uint, week int) ([]models.TrainingWorkout, error) {
var workouts []models.TrainingWorkout
err := r.db.Where("plan_id = ? AND week = ?", planID, week).Find(&workouts).Error
if err != nil {
return nil, err
}
return workouts, nil
}
// GetCompletedWorkouts возвращает завершенные тренировки плана
func (r *TrainingPlanRepository) GetCompletedWorkouts(planID uint) ([]models.TrainingWorkout, error) {
var workouts []models.TrainingWorkout
err := r.db.Where("plan_id = ? AND completed = ?", planID, true).Find(&workouts).Error
if err != nil {
return nil, err
}
return workouts, nil
}
@@ -0,0 +1,126 @@
package repository
import (
"api_bb/internal/models"
"fmt"
"gorm.io/gorm"
)
type UserRepository interface {
Create(user *models.User) error
FindByID(id uint) (*models.User, error)
FindByEmail(email string) (*models.User, error)
Update(user *models.User) error
Delete(id uint) error
UpdateExcludeEmail(userUpdate *models.User) error
UpdateAvatar(userID uint, avatarPath string) error
FindAll() ([]models.User, error)
MarkEmailAsVerified(userID uint) error
UpdatePassword(userID uint, newPassword string) error
GetUserByID(id uint) (*models.User, error)
}
func (r *userRepository) UpdateAvatar(userID uint, avatarPath string) error {
result := r.db.Model(&models.User{}).Where("id = ?", userID).Update("avatar", avatarPath)
if result.Error != nil {
return result.Error
}
if result.RowsAffected == 0 {
return fmt.Errorf("user not found")
}
return nil
}
type userRepository struct {
db *gorm.DB
}
func NewUserRepository(db *gorm.DB) UserRepository {
return &userRepository{db: db}
}
// Add to userRepository implementation
func (r *userRepository) FindAll() ([]models.User, error) {
var users []models.User
err := r.db.Find(&users).Error
return users, err
}
func (r *userRepository) Create(user *models.User) error {
return r.db.Create(user).Error
}
func (r *userRepository) FindByID(id uint) (*models.User, error) {
var user models.User
err := r.db.First(&user, id).Error
return &user, err
}
func (r *userRepository) FindByEmail(email string) (*models.User, error) {
var user models.User
err := r.db.Where("email = ?", email).First(&user).Error
return &user, err
}
func (r *userRepository) Update(user *models.User) error {
return r.db.Save(user).Error
}
func (r *userRepository) Delete(id uint) error {
return r.db.Delete(&models.User{}, id).Error
}
// repository/user_repository.go
func (r *userRepository) UpdateExcludeEmail(userUpdate *models.User) error {
result := r.db.Model(userUpdate).Where("id = ?", userUpdate.ID).Updates(map[string]interface{}{
"first_name": userUpdate.FirstName,
"last_name": userUpdate.LastName,
"avatar": userUpdate.Avatar, // Добавить обновление аватара
"phone": userUpdate.Phone,
"experience": userUpdate.Experience,
"goals": userUpdate.Goals,
"newsletter": userUpdate.Newsletter,
"updated_at": userUpdate.UpdatedAt,
})
if result.Error != nil {
return result.Error
}
if result.RowsAffected == 0 {
return fmt.Errorf("user not found")
}
return nil
}
// MarkEmailAsVerified помечает email пользователя как верифицированный
func (r userRepository) MarkEmailAsVerified(userID uint) error {
result := r.db.Model(&models.User{}).Where("id = ?", userID).Update("email_verified", true)
if result.Error != nil {
return result.Error
}
if result.RowsAffected == 0 {
return fmt.Errorf("user not found")
}
return nil
}
// UpdatePassword обновляет пароль пользователя
func (r userRepository) UpdatePassword(userID uint, newPassword string) error {
result := r.db.Model(&models.User{}).Where("id = ?", userID).Update("password", newPassword)
if result.Error != nil {
return result.Error
}
if result.RowsAffected == 0 {
return fmt.Errorf("user not found")
}
return nil
}
func (r userRepository) GetUserByID(id uint) (*models.User, error) {
var user models.User
err := r.db.First(&user, id).Error
return &user, err
}
@@ -0,0 +1,214 @@
// repositories/user_stats_repository.go
package repository
import (
"time"
"api_bb/internal/models"
"api_bb/pkg/utils"
"gorm.io/gorm"
)
type UserStatsRepository interface {
Create(userStats *models.UserStats) error
GetByID(id uint) (*models.UserStats, error)
GetByUserID(userID uint) (*models.UserStats, error)
Update(userStats *models.UserStats) error
Delete(id uint) error
UpdateStreaks(userID uint, lastWorkout time.Time) error
UpdateWeeklyDistance(userID uint, distance float64) error
UpdateMonthlyDistance(userID uint, distance float64) error
IncrementWorkouts(userID uint, distance float64, duration int) error
UpdatePersonalBest(userID uint, distanceType string, time string) error
GetUserStatsResponse(userID uint) (*models.UserStatsResponse, error)
GetByUserIDOrCreate(userID uint) (*models.UserStats, error)
}
type userStatsRepository struct {
db *gorm.DB
}
func NewUserStatsRepository(db *gorm.DB) UserStatsRepository {
return &userStatsRepository{db: db}
}
// GetByUserIDOrCreate возвращает статистику по ID пользователя или создает новую
func (r *userStatsRepository) GetByUserIDOrCreate(userID uint) (*models.UserStats, error) {
userStats, err := r.GetByUserID(userID)
if err != nil {
if err == gorm.ErrRecordNotFound {
// Создаем новую статистику
newStats := &models.UserStats{
UserID: userID,
TotalDistance: 0,
TotalTime: 0,
AvgPace: "0:00",
WorkoutsCount: 0,
CurrentStreak: 0,
LongestStreak: 0,
WeeklyDistance: 0,
MonthlyDistance: 0,
Best5K: "",
Best10K: "",
BestHalf: "",
BestMarathon: "",
LastWorkout: time.Time{},
}
if err := r.Create(newStats); err != nil {
return nil, err
}
return newStats, nil
}
return nil, err
}
return userStats, nil
}
// Create создает новую статистику пользователя
func (r *userStatsRepository) Create(userStats *models.UserStats) error {
return r.db.Create(userStats).Error
}
// GetByID возвращает статистику по ID
func (r *userStatsRepository) GetByID(id uint) (*models.UserStats, error) {
var userStats models.UserStats
err := r.db.First(&userStats, id).Error
if err != nil {
return nil, err
}
return &userStats, nil
}
// GetByUserID возвращает статистику по ID пользователя
func (r *userStatsRepository) GetByUserID(userID uint) (*models.UserStats, error) {
var userStats models.UserStats
err := r.db.Where("user_id = ?", userID).First(&userStats).Error
if err != nil {
return nil, err
}
return &userStats, nil
}
// Update обновляет статистику пользователя
func (r *userStatsRepository) Update(userStats *models.UserStats) error {
return r.db.Save(userStats).Error
}
// Delete удаляет статистику пользователя
func (r *userStatsRepository) Delete(id uint) error {
return r.db.Delete(&models.UserStats{}, id).Error
}
// UpdateStreaks обновляет серии тренировок
func (r *userStatsRepository) UpdateStreaks(userID uint, lastWorkout time.Time) error {
userStats, err := r.GetByUserID(userID)
if err != nil {
return err
}
// Проверяем, была ли тренировка вчера
yesterday := time.Now().AddDate(0, 0, -1)
if userStats.LastWorkout.Format("2006-01-02") == yesterday.Format("2006-01-02") {
// Продолжаем серию
userStats.CurrentStreak++
} else if userStats.LastWorkout.Format("2006-01-02") != time.Now().Format("2006-01-02") {
// Сбрасываем серию, если не было тренировки сегодня или вчера
userStats.CurrentStreak = 1
}
// Обновляем самую длинную серию
if userStats.CurrentStreak > userStats.LongestStreak {
userStats.LongestStreak = userStats.CurrentStreak
}
userStats.LastWorkout = lastWorkout
return r.Update(userStats)
}
// UpdateWeeklyDistance обновляет недельный пробег
func (r *userStatsRepository) UpdateWeeklyDistance(userID uint, distance float64) error {
return r.db.Model(&models.UserStats{}).
Where("user_id = ?", userID).
Update("weekly_distance", gorm.Expr("weekly_distance + ?", distance)).
Error
}
// UpdateMonthlyDistance обновляет месячный пробег
func (r *userStatsRepository) UpdateMonthlyDistance(userID uint, distance float64) error {
return r.db.Model(&models.UserStats{}).
Where("user_id = ?", userID).
Update("monthly_distance", gorm.Expr("monthly_distance + ?", distance)).
Error
}
// IncrementWorkouts увеличивает счетчик тренировок и обновляет общие показатели
func (r *userStatsRepository) IncrementWorkouts(userID uint, distance float64, duration int) error {
userStats, err := r.GetByUserID(userID)
if err != nil {
return err
}
// Обновляем общие показатели
userStats.WorkoutsCount++
userStats.TotalDistance += distance
userStats.TotalTime += duration
// Пересчитываем средний темп (в минутах на км)
if userStats.TotalDistance > 0 {
avgPaceMinPerKm := float64(userStats.TotalTime) / userStats.TotalDistance
minutes := int(avgPaceMinPerKm)
seconds := int((avgPaceMinPerKm - float64(minutes)) * 60)
userStats.AvgPace = utils.FormatPace(minutes, seconds)
}
return r.Update(userStats)
}
// UpdatePersonalBest обновляет личный рекорд
func (r *userStatsRepository) UpdatePersonalBest(userID uint, distanceType string, time string) error {
updateField := ""
switch distanceType {
case "5k":
updateField = "best_5k"
case "10k":
updateField = "best_10k"
case "half":
updateField = "best_half"
case "marathon":
updateField = "best_marathon"
default:
return nil
}
return r.db.Model(&models.UserStats{}).
Where("user_id = ?", userID).
Update(updateField, time).
Error
}
// GetUserStatsResponse возвращает статистику в формате DTO
func (r *userStatsRepository) GetUserStatsResponse(userID uint) (*models.UserStatsResponse, error) {
userStats, err := r.GetByUserIDOrCreate(userID)
if err != nil {
return nil, err
}
return &models.UserStatsResponse{
TotalDistance: userStats.TotalDistance,
TotalTime: userStats.TotalTime,
AvgPace: userStats.AvgPace,
WorkoutsCount: userStats.WorkoutsCount,
CurrentStreak: userStats.CurrentStreak,
LongestStreak: userStats.LongestStreak,
WeeklyDistance: userStats.WeeklyDistance,
MonthlyDistance: userStats.MonthlyDistance,
PersonalBests: models.PersonalBestsSummary{
Best5K: userStats.Best5K,
Best10K: userStats.Best10K,
BestHalf: userStats.BestHalf,
BestMarathon: userStats.BestMarathon,
},
}, nil
}
@@ -0,0 +1,169 @@
// repositories/workout_repository.go
package repository
import (
"api_bb/internal/models"
"errors"
"fmt"
"time"
"gorm.io/gorm"
)
type WorkoutRepository interface {
Create(workout *models.Workout) error
FindByID(id uint) (*models.Workout, error)
FindByUserID(userID uint) ([]models.Workout, error)
Update(workout *models.Workout) error
Delete(id uint) error
GetWorkoutStats(userID uint) (*models.WorkoutStatsResponse, error)
FindByDateRange(userID uint, startDate, endDate time.Time) ([]models.Workout, error)
GetMonthlyStats(userID uint, year int) ([]models.MonthlyStat, error)
GetLatestWorkouts(userID uint, limit int) ([]models.Workout, error)
GetByType(userID uint, workoutType models.WorkoutType) ([]models.Workout, error)
}
type workoutRepository struct {
db *gorm.DB
}
var (
ErrNotFound = errors.New("record not found")
)
func NewWorkoutRepository(db *gorm.DB) WorkoutRepository {
return &workoutRepository{db: db}
}
func (r *workoutRepository) Create(workout *models.Workout) error {
return r.db.Create(workout).Error
}
func (r *workoutRepository) FindByID(id uint) (*models.Workout, error) {
var workout models.Workout
err := r.db.Preload("User").First(&workout, id).Error
if err != nil {
return nil, err
}
return &workout, nil
}
func (r *workoutRepository) FindByUserID(userID uint) ([]models.Workout, error) {
var workouts []models.Workout
err := r.db.Preload("User").Where("user_id = ?", userID).Order("date DESC").Find(&workouts).Error
if err != nil {
return nil, err
}
return workouts, nil
}
func (r *workoutRepository) Update(workout *models.Workout) error {
return r.db.Save(workout).Error
}
func (r *workoutRepository) Delete(id uint) error {
return r.db.Delete(&models.Workout{}, id).Error
}
func (r *workoutRepository) GetWorkoutStats(userID uint) (*models.WorkoutStatsResponse, error) {
var stats models.WorkoutStatsResponse
// Получаем общее количество тренировок
var totalWorkouts int64
if err := r.db.Model(&models.Workout{}).Where("user_id = ?", userID).Count(&totalWorkouts).Error; err != nil {
return nil, err
}
stats.TotalWorkouts = int(totalWorkouts)
// Получаем общую дистанцию
var totalDistance struct{ Total float64 }
if err := r.db.Model(&models.Workout{}).Where("user_id = ?", userID).Select("COALESCE(SUM(distance_km), 0) as total").Scan(&totalDistance).Error; err != nil {
return nil, err
}
stats.TotalDistance = totalDistance.Total
// Получаем общее время
var totalTime struct{ Total int }
if err := r.db.Model(&models.Workout{}).Where("user_id = ?", userID).Select("COALESCE(SUM(duration_min), 0) as total").Scan(&totalTime).Error; err != nil {
return nil, err
}
stats.TotalTime = totalTime.Total
// Получаем месячную статистику
monthlyStats, err := r.GetMonthlyStats(userID, time.Now().Year())
if err != nil {
return nil, err
}
stats.MonthlyStats = monthlyStats
// Рассчитываем средний темп (упрощенная версия)
if totalDistance.Total > 0 && totalTime.Total > 0 {
avgPaceMinPerKm := float64(totalTime.Total) / totalDistance.Total
minutes := int(avgPaceMinPerKm)
seconds := int((avgPaceMinPerKm - float64(minutes)) * 60)
stats.AveragePace = fmt.Sprintf("%d:%02d", minutes, seconds)
} else {
stats.AveragePace = "0:00"
}
return &stats, nil
}
func (r *workoutRepository) FindByDateRange(userID uint, startDate, endDate time.Time) ([]models.Workout, error) {
var workouts []models.Workout
err := r.db.Preload("User").
Where("user_id = ? AND date BETWEEN ? AND ?", userID, startDate, endDate).
Order("date DESC").
Find(&workouts).Error
if err != nil {
return nil, err
}
return workouts, nil
}
func (r *workoutRepository) GetMonthlyStats(userID uint, year int) ([]models.MonthlyStat, error) {
var monthlyStats []models.MonthlyStat
query := `
SELECT
TO_CHAR(date, 'YYYY-MM') as month,
COALESCE(SUM(distance_km), 0) as distance,
COUNT(*) as workouts
FROM workouts
WHERE user_id = ? AND EXTRACT(YEAR FROM date) = ?
GROUP BY TO_CHAR(date, 'YYYY-MM')
ORDER BY month DESC
`
err := r.db.Raw(query, userID, year).Scan(&monthlyStats).Error
if err != nil {
return nil, err
}
return monthlyStats, nil
}
func (r *workoutRepository) GetLatestWorkouts(userID uint, limit int) ([]models.Workout, error) {
var workouts []models.Workout
err := r.db.Preload("User").
Where("user_id = ?", userID).
Order("date DESC").
Limit(limit).
Find(&workouts).Error
if err != nil {
return nil, err
}
return workouts, nil
}
func (r *workoutRepository) GetByType(userID uint, workoutType models.WorkoutType) ([]models.Workout, error) {
var workouts []models.Workout
err := r.db.Preload("User").
Where("user_id = ? AND type = ?", userID, workoutType).
Order("date DESC").
Find(&workouts).Error
if err != nil {
return nil, err
}
return workouts, nil
}
+305
View File
@@ -0,0 +1,305 @@
// routes/routes.go
package routes
import (
"net/http"
"github.com/go-chi/chi/v5"
"gorm.io/gorm"
"api_bb/internal/config"
"api_bb/internal/handlers"
"api_bb/internal/repository"
"api_bb/internal/service"
"api_bb/pkg/logger"
"api_bb/pkg/middleware"
)
func SetupRouter(db *gorm.DB, config *config.Config) http.Handler {
r := chi.NewRouter()
// Apply common middleware
for _, m := range middleware.CommonMiddleware() {
r.Use(m)
}
// handler
h := handlers.NewHandler(db, config)
// Serve static files (avatars)
r.Handle("/uploads/*", http.StripPrefix("/uploads/",
http.FileServer(http.Dir("./uploads"))))
// Initialize repositories
userRepo := repository.NewUserRepository(db)
// Initialize logger
baseLogger := logger.NewWrapper(logger.Get())
// Initialize services with logger
jwtService := service.NewJWTService(config.JWTSecret)
// Email service initialization with fallback
var emailHandler *handlers.EmailHandler
if h.EmailHandler() != nil {
emailHandler = h.EmailHandler()
}
// Health routes
r.Route("/api", func(r chi.Router) {
r.Get("/health", h.HealthHandler().HealthCheck)
r.Get("/check", h.HealthHandler().Check)
})
// API v1 routes
r.Route("/v1", func(r chi.Router) {
// Email verification (public) - только если доступен
if emailHandler != nil {
r.Get("/verify-email", emailHandler.VerifyEmail)
}
// Public auth routes
r.Route("/auth", func(r chi.Router) {
r.Post("/register", h.AuthHandler().Register)
r.Post("/login", h.AuthHandler().Login)
r.Post("/logout", h.AuthHandler().Logout)
// Email routes (only if email handler is available)
if emailHandler != nil {
r.Post("/verify-email/resend", emailHandler.ResendVerification)
r.Post("/password-reset/request", emailHandler.RequestPasswordReset)
r.Post("/password-reset/confirm", emailHandler.ConfirmPasswordReset)
}
})
// Публичные маршруты для достижений (если нужны)
r.Route("/achievements", func(r chi.Router) {
// Публичные маршруты для просмотра достижений других пользователей
r.Get("/user/{userID}", h.UserAchievementHandler().GetPublicUserAchievements)
r.Get("/user/{userID}/summary", h.UserAchievementHandler().GetPublicUserAchievementsSummary)
r.Get("/user/{userID}/recent", h.UserAchievementHandler().GetPublicRecentAchievements)
})
// Protected routes
r.Route("/user", func(r chi.Router) {
r.Use(middleware.AuthMiddleware(jwtService, userRepo))
r.Use(middleware.RequireAuth)
// user profile routes
r.Get("/profile", h.UserHandler().GetProfile)
r.Post("/editProfile", h.UserHandler().UpdateProfile)
r.Get("/", h.UserHandler().GetUsers)
// Все операции с аватарами теперь через AvatarHandler
r.Route("/avatars", func(r chi.Router) {
r.Post("/upload", h.AvatarHandler().UploadAvatar)
r.Delete("/delete", h.AvatarHandler().DeleteAvatar)
r.Get("/{filename}", h.AvatarHandler().GetAvatar)
})
r.Route("/stats", func(r chi.Router) {
r.Get("/", h.UserStatsHandler().GetUserStats)
r.Get("/{userID}", h.UserStatsHandler().GetUserStatsByID)
r.Post("/workout", h.UserStatsHandler().IncrementWorkout)
r.Put("/personal-best", h.UserStatsHandler().UpdatePersonalBest)
r.Post("/weekly/reset", h.UserStatsHandler().ResetWeeklyDistance)
r.Post("/monthly/reset", h.UserStatsHandler().ResetMonthlyDistance)
})
// Маршруты для тренировок
r.Route("/workouts", func(r chi.Router) {
r.Post("/", h.UserWorkoutHandler().CreateWorkout)
r.Get("/", h.UserWorkoutHandler().GetWorkouts)
r.Get("/stats", h.UserWorkoutHandler().GetWorkoutStats)
r.Get("/type/{type}", h.UserWorkoutHandler().GetWorkoutsByType)
r.Route("/{id}", func(r chi.Router) {
r.Get("/", h.UserWorkoutHandler().GetWorkoutByID)
r.Put("/", h.UserWorkoutHandler().UpdateWorkout)
r.Delete("/", h.UserWorkoutHandler().DeleteWorkout)
})
})
// Маршруты для достижений (achievements)
r.Route("/achievements", func(r chi.Router) {
// Создание нового достижения
r.Post("/", h.UserAchievementHandler().CreateAchievement)
// Получение всех достижений пользователя
r.Get("/", h.UserAchievementHandler().GetUserAchievements)
// Получение сводки по достижениям
r.Get("/summary", h.UserAchievementHandler().GetUserAchievementsSummary)
// Получение последних достижений (с опциональным лимитом)
r.Get("/recent", h.UserAchievementHandler().GetRecentAchievements)
// Получение достижений по типу
r.Get("/type/{type}", h.UserAchievementHandler().GetAchievementsByType)
// Операции с конкретным достижением
r.Route("/{id}", func(r chi.Router) {
// Получение достижения по ID
r.Get("/", h.UserAchievementHandler().GetAchievementByID)
// Обновление достижения
r.Put("/", h.UserAchievementHandler().UpdateAchievement)
// Удаление достижения
r.Delete("/", h.UserAchievementHandler().DeleteAchievement)
// Подтверждение достижения
r.Patch("/verify", h.UserAchievementHandler().VerifyAchievement)
})
})
// Personal Best routes
r.Route("/personal-bests", func(r chi.Router) {
// CRUD operations
r.Post("/", h.PersonalBestHandler().CreatePersonalBest)
r.Get("/", h.PersonalBestHandler().GetUserPersonalBests)
r.Get("/recent", h.PersonalBestHandler().GetRecentPersonalBests)
r.Get("/summary", h.PersonalBestHandler().GetPersonalBestsSummary)
r.Post("/calculate-pace", h.PersonalBestHandler().CalculatePace)
// Distance-specific routes
r.Route("/distance/{distanceType}", func(r chi.Router) {
r.Get("/", h.PersonalBestHandler().GetPersonalBestsByDistance)
r.Get("/best", h.PersonalBestHandler().GetBestByDistance)
})
// Individual personal best routes
r.Route("/{id}", func(r chi.Router) {
r.Get("/", h.PersonalBestHandler().GetPersonalBest)
r.Put("/", h.PersonalBestHandler().UpdatePersonalBest)
r.Delete("/", h.PersonalBestHandler().DeletePersonalBest)
r.Patch("/verify", h.PersonalBestHandler().VerifyPersonalBest)
})
})
// Маршруты для тренировочных планов (Training Plans)
r.Route("/training-plans", func(r chi.Router) {
// Создание нового тренировочного плана
r.Post("/", h.TrainingPlanHandler().CreateTrainingPlan)
// Получение всех тренировочных планов пользователя
r.Get("/", h.TrainingPlanHandler().GetTrainingPlans)
// Получение активного тренировочного плана
r.Get("/active", h.TrainingPlanHandler().GetActiveTrainingPlan)
// Обновление текущей недели плана
r.Patch("/current-week", h.TrainingPlanHandler().UpdateCurrentWeek)
// Операции с конкретным тренировочным планом
r.Route("/{id}", func(r chi.Router) {
// Получение тренировочного плана по ID
r.Get("/", h.TrainingPlanHandler().GetTrainingPlanByID)
// Обновление тренировочного плана
r.Put("/", h.TrainingPlanHandler().UpdateTrainingPlan)
// Удаление тренировочного плана
r.Delete("/", h.TrainingPlanHandler().DeleteTrainingPlan)
// Пометить план как завершенный
r.Patch("/complete", h.TrainingPlanHandler().MarkTrainingPlanAsCompleted)
})
})
})
r.Route("/news", func(r chi.Router) {
// Публичные маршруты
r.Get("/", h.NewsHandler().GetNews)
r.Get("/{id}", h.NewsHandler().GetNewsByID)
r.Get("/{id}/comments", h.NewsHandler().GetComments)
r.Get("/check", h.HealthHandler().Check)
// Защищенные маршруты
r.Group(func(r chi.Router) {
r.Use(middleware.AuthMiddleware(jwtService, userRepo))
r.Use(middleware.RequireAuth)
// News EndPoints
r.Post("/", h.NewsHandler().CreateNews)
r.Put("/{id}", h.NewsHandler().UpdateNews)
r.Delete("/{id}", h.NewsHandler().DeleteNews)
r.Get("/my/news", h.NewsHandler().GetUserNews)
r.Post("/{id}/comments", h.NewsHandler().CreateComment)
r.Delete("/comments/{commentId}", h.NewsHandler().DeleteComment)
r.Get("/check", h.HealthHandler().Check)
})
})
// Маршруты для отзывов
r.Route("/reviews", func(r chi.Router) {
// Публичные маршруты
r.Get("/", h.ReviewHandler().GetReviews)
r.Get("/stats", h.ReviewHandler().GetReviewsStats)
r.Get("/{id}", h.ReviewHandler().GetReviewByID)
// Защищенные маршруты
r.Group(func(r chi.Router) {
r.Use(middleware.AuthMiddleware(jwtService, userRepo))
r.Use(middleware.RequireAuth)
r.Post("/", h.ReviewHandler().CreateReview)
r.Get("/my", h.ReviewHandler().GetMyReviews)
r.Put("/{id}", h.ReviewHandler().UpdateReview)
r.Delete("/{id}", h.ReviewHandler().DeleteReview)
})
})
// Events
r.Route("/events", func(r chi.Router) {
// Публичные маршруты
r.Get("/", h.EventHandler().GetAllEvents)
r.Get("/upcoming", h.EventHandler().GetUpcomingEvents)
r.Get("/type/{type}", h.EventHandler().GetEventsByType)
r.Get("/{id}", h.EventHandler().GetEvent)
r.Get("/{eventId}/availability", h.EventRegistrationHandler().CheckEventAvailability)
// Защищенные маршруты (требуют аутентификации)
r.Group(func(r chi.Router) {
r.Use(middleware.AuthMiddleware(jwtService, userRepo))
r.Use(middleware.RequireAuth)
// Регистрации пользователя
r.Post("/register", h.EventRegistrationHandler().RegisterForEvent)
r.Get("/my/registrations", h.EventRegistrationHandler().GetUserRegistrations)
r.Delete("/registrations/{id}", h.EventRegistrationHandler().CancelRegistration)
r.Get("/registrations/{id}", h.EventRegistrationHandler().GetRegistration)
})
// Админские маршруты
r.Group(func(r chi.Router) {
r.Use(middleware.AuthMiddleware(jwtService, userRepo))
r.Use(middleware.RequireAuth)
r.Use(middleware.AdminMiddleware)
// Управление событиями
r.Post("/", h.EventHandler().CreateEvent)
r.Put("/{id}", h.EventHandler().UpdateEvent)
r.Delete("/{id}", h.EventHandler().DeleteEvent)
r.Patch("/{id}/registration-status", h.EventHandler().ToggleRegistrationStatus)
// Управление регистрациями
r.Get("/{eventId}/registrations", h.EventRegistrationHandler().GetEventRegistrations)
r.Patch("/registrations/{id}/status", h.EventRegistrationHandler().UpdateRegistrationStatus)
r.Patch("/registrations/{id}/result-time", h.EventRegistrationHandler().UpdateResultTime)
})
})
})
// Логируем все зарегистрированные маршруты
routeLogger := logger.NewRouteLogger(baseLogger)
routeLogger.LogRoutes(r)
return r
}
@@ -0,0 +1,74 @@
// scripts/migrate_existing_users.go
package main
import (
"api_bb/internal/models"
"api_bb/internal/repository"
"api_bb/pkg/logger"
"gorm.io/gorm"
"go.uber.org/zap"
)
func MigrateExistingUsers(db *gorm.DB) error {
log := logger.NewWrapper(logger.Get().With(zap.String("script", "migrate_existing_users")))
userRepo := repository.NewUserRepository(db)
userStatsRepo := repository.NewUserStatsRepository(db)
// Получаем всех пользователей
users, err := userRepo.FindAll()
if err != nil {
return err
}
log.Info("starting migration for existing users",
zap.Int("total_users", len(users)))
successCount := 0
for _, user := range users {
// Проверяем, есть ли уже статистика
_, err := userStatsRepo.GetByUserID(user.ID)
if err == gorm.ErrRecordNotFound {
// Создаем статистику
userStats := &models.UserStats{
UserID: user.ID,
TotalDistance: 0,
TotalTime: 0,
AvgPace: "0:00",
WorkoutsCount: 0,
CurrentStreak: 0,
LongestStreak: 0,
WeeklyDistance: 0,
MonthlyDistance: 0,
Best5K: "",
Best10K: "",
BestHalf: "",
BestMarathon: "",
LastWorkout: user.CreatedAt, // Используем дату создания как последнюю тренировку
}
if err := userStatsRepo.Create(userStats); err != nil {
log.Error("failed to create stats for user",
zap.Uint("user_id", user.ID),
zap.Error(err))
continue
}
successCount++
log.Info("created stats for user",
zap.Uint("user_id", user.ID),
zap.String("email", user.Email))
} else if err != nil {
log.Error("error checking stats for user",
zap.Uint("user_id", user.ID),
zap.Error(err))
}
}
log.Info("migration completed",
zap.Int("successful_creations", successCount),
zap.Int("total_users", len(users)))
return nil
}
@@ -0,0 +1,181 @@
// service/achievement_service.go (дополнение)
package service
import (
"api_bb/internal/models"
"api_bb/internal/repository"
"errors"
)
type AchievementService struct {
achievementRepo repository.AchievementRepository
}
func NewAchievementService(achievementRepo repository.AchievementRepository) *AchievementService {
return &AchievementService{
achievementRepo: achievementRepo,
}
}
// CreateAchievement создает новое достижение
func (s *AchievementService) CreateAchievement(userID uint, req models.AchievementCreateRequest) (*models.Achievement, error) {
// Проверяем, нет ли уже достижения с таким названием у пользователя
exists, err := s.achievementRepo.ExistsByTitleAndUser(userID, req.Title)
if err != nil {
return nil, err
}
if exists {
return nil, ErrAchievementAlreadyExists
}
achievement := &models.Achievement{
UserID: userID,
Type: req.Type,
Title: req.Title,
Description: req.Description,
Result: req.Result,
Distance: req.Distance,
Date: req.Date,
BadgeImage: req.BadgeImage,
Verified: false, // По умолчанию не подтверждено
}
if err := s.achievementRepo.Create(achievement); err != nil {
return nil, err
}
return achievement, nil
}
// GetVerifiedAchievements возвращает только подтвержденные достижения пользователя
func (s *AchievementService) GetVerifiedAchievements(userID uint) ([]models.Achievement, error) {
return s.achievementRepo.GetVerifiedByUserID(userID)
}
// GetVerifiedRecentAchievements возвращает последние подтвержденные достижения
func (s *AchievementService) GetVerifiedRecentAchievements(userID uint, limit int) ([]models.Achievement, error) {
achievements, err := s.achievementRepo.GetRecentAchievements(userID, limit)
if err != nil {
return nil, err
}
// Фильтруем только подтвержденные
var verified []models.Achievement
for _, achievement := range achievements {
if achievement.Verified {
verified = append(verified, achievement)
}
}
return verified, nil
}
// GetUserAchievements возвращает все достижения пользователя
func (s *AchievementService) GetUserAchievements(userID uint) ([]models.Achievement, error) {
return s.achievementRepo.GetByUserID(userID)
}
// GetUserAchievementsSummary возвращает сводку по достижениям пользователя
func (s *AchievementService) GetUserAchievementsSummary(userID uint) (*models.UserAchievementsResponse, error) {
return s.achievementRepo.GetUserAchievementsSummary(userID)
}
// VerifyAchievement подтверждает достижение
func (s *AchievementService) VerifyAchievement(achievementID uint, userID uint) error {
// Проверяем, что достижение принадлежит пользователю
achievement, err := s.achievementRepo.GetByID(achievementID)
if err != nil {
return err
}
if achievement.UserID != userID {
return ErrAchievementNotFound
}
return s.achievementRepo.VerifyAchievement(achievementID)
}
// GetRecentAchievements возвращает последние достижения
func (s *AchievementService) GetRecentAchievements(userID uint, limit int) ([]models.Achievement, error) {
return s.achievementRepo.GetRecentAchievements(userID, limit)
}
// GetAchievementsByType возвращает достижения по типу
func (s *AchievementService) GetAchievementsByType(userID uint, achievementType models.AchievementType) ([]models.Achievement, error) {
return s.achievementRepo.GetByUserAndType(userID, achievementType)
}
// DeleteAchievement удаляет достижение
func (s *AchievementService) DeleteAchievement(achievementID uint, userID uint) error {
// Проверяем, что достижение принадлежит пользователю
achievement, err := s.achievementRepo.GetByID(achievementID)
if err != nil {
return err
}
if achievement.UserID != userID {
return ErrAchievementNotFound
}
return s.achievementRepo.Delete(achievementID)
}
// GetAchievementByID возвращает достижение по ID
func (s *AchievementService) GetAchievementByID(achievementID uint, userID uint) (*models.Achievement, error) {
achievement, err := s.achievementRepo.GetByID(achievementID)
if err != nil {
return nil, err
}
// Проверяем, что достижение принадлежит пользователю
if achievement.UserID != userID {
return nil, ErrAchievementNotFound
}
return achievement, nil
}
// UpdateAchievement обновляет достижение
func (s *AchievementService) UpdateAchievement(achievementID uint, userID uint, req models.AchievementCreateRequest) (*models.Achievement, error) {
// Проверяем, что достижение принадлежит пользователю
existingAchievement, err := s.achievementRepo.GetByID(achievementID)
if err != nil {
return nil, err
}
if existingAchievement.UserID != userID {
return nil, ErrAchievementNotFound
}
// Проверяем, нет ли другого достижения с таким названием
if existingAchievement.Title != req.Title {
exists, err := s.achievementRepo.ExistsByTitleAndUser(userID, req.Title)
if err != nil {
return nil, err
}
if exists {
return nil, ErrAchievementAlreadyExists
}
}
// Обновляем данные
existingAchievement.Type = req.Type
existingAchievement.Title = req.Title
existingAchievement.Description = req.Description
existingAchievement.Result = req.Result
existingAchievement.Distance = req.Distance
existingAchievement.Date = req.Date
existingAchievement.BadgeImage = req.BadgeImage
if err := s.achievementRepo.Update(existingAchievement); err != nil {
return nil, err
}
return existingAchievement, nil
}
// Ошибки
var (
ErrAchievementAlreadyExists = errors.New("achievement with this title already exists")
ErrAchievementNotFound = errors.New("achievement not found")
)
@@ -0,0 +1,122 @@
// service/auth_service.go
package service
import (
"errors"
"api_bb/internal/models"
"api_bb/internal/repository"
"api_bb/pkg/logger"
"go.uber.org/zap"
"golang.org/x/crypto/bcrypt"
)
type AuthService interface {
Register(user *models.User) error
Login(email, password string) (*models.User, string, error)
}
type authService struct {
userRepo repository.UserRepository
jwtService JWTService
logger logger.LoggerInterface
}
func NewAuthService(userRepo repository.UserRepository, jwtService JWTService, log logger.LoggerInterface) AuthService {
// Создаем логгер с контекстом для сервиса
serviceLogger := log.With(zap.String("service", "auth"))
return &authService{
userRepo: userRepo,
jwtService: jwtService,
logger: serviceLogger,
}
}
func (s *authService) Register(user *models.User) error {
s.logger.Info("Registering new user",
zap.String("email", user.Email),
)
existingUser, err := s.userRepo.FindByEmail(user.Email)
if err == nil && existingUser != nil {
s.logger.Warn("Registration failed - email already exists",
zap.String("email", user.Email),
)
return errors.New("user with this email already exists")
}
err = s.userRepo.Create(user)
if err != nil {
s.logger.Error("Failed to create user in database",
zap.String("email", user.Email),
zap.Error(err),
)
return err
}
s.logger.Info("User registered successfully",
zap.Uint("user_id", user.ID),
zap.String("email", user.Email),
)
return nil
}
func (s *authService) Login(email, password string) (*models.User, string, error) {
s.logger.Info("Login attempt",
zap.String("email", email),
zap.Int("password_length", len(password)),
)
user, err := s.userRepo.FindByEmail(email)
if err != nil {
s.logger.Warn("Login failed - user not found",
zap.String("email", email),
zap.Error(err),
)
return nil, "", errors.New("invalid email")
}
s.logger.Debug("User found for login",
zap.Uint("user_id", user.ID),
zap.String("stored_hash_prefix", user.Password[:min(10, len(user.Password))]),
)
// Проверяем пароль
err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password))
if err != nil {
s.logger.Warn("Login failed - invalid password",
zap.Uint("user_id", user.ID),
zap.String("email", email),
zap.Error(err),
)
return nil, "", errors.New("invalid password")
}
s.logger.Info("Login successful",
zap.Uint("user_id", user.ID),
zap.String("email", email),
)
token, err := s.jwtService.GenerateToken(user.ID, user.Email)
if err != nil {
s.logger.Error("Failed to generate JWT token",
zap.Uint("user_id", user.ID),
zap.Error(err),
)
return nil, "", err
}
return user, token, nil
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
@@ -0,0 +1,215 @@
// service/avatar_service.go
package service
import (
"api_bb/internal/repository"
"api_bb/pkg/logger"
"fmt"
"io"
"mime/multipart"
"os"
"path/filepath"
"strings"
"time"
"go.uber.org/zap"
)
type AvatarService interface {
UploadAvatar(userID uint, file multipart.File, header *multipart.FileHeader) (string, error)
DeleteAvatar(userID uint) error
GetAvatarPath(userID uint) (string, error)
GetAvatarFile(filename string) ([]byte, string, error)
ServeAvatarFile(w io.Writer, filename string) (string, error)
}
type avatarService struct {
userRepo repository.UserRepository
logger logger.LoggerInterface
}
func NewAvatarService(userRepo repository.UserRepository, log logger.LoggerInterface) AvatarService {
return &avatarService{
userRepo: userRepo,
logger: log.With(zap.String("service", "avatar")),
}
}
func (s *avatarService) UploadAvatar(userID uint, file multipart.File, header *multipart.FileHeader) (string, error) {
// Проверяем пользователя
user, err := s.userRepo.FindByID(userID)
if err != nil {
return "", fmt.Errorf("user not found")
}
// Создаем директорию для аватаров если не существует
uploadDir := "./uploads/avatars"
if err := os.MkdirAll(uploadDir, 0755); err != nil {
return "", fmt.Errorf("failed to create upload directory: %v", err)
}
// Генерируем уникальное имя файла
fileExt := filepath.Ext(header.Filename)
fileName := fmt.Sprintf("avatar_%d_%d%s", userID, time.Now().Unix(), fileExt)
filePath := filepath.Join(uploadDir, fileName)
// Создаем файл
dst, err := os.Create(filePath)
if err != nil {
return "", fmt.Errorf("failed to create file: %v", err)
}
defer dst.Close()
// Копируем содержимое
if _, err := io.Copy(dst, file); err != nil {
return "", fmt.Errorf("failed to save file: %v", err)
}
// Удаляем старый аватар если существует
if user.Avatar != "" {
oldPath := strings.TrimPrefix(user.Avatar, "/")
if _, err := os.Stat(oldPath); err == nil {
os.Remove(oldPath)
}
}
// Сохраняем путь в БД
avatarPath := "/uploads/avatars/" + fileName
if err := s.userRepo.UpdateAvatar(userID, avatarPath); err != nil {
// Если не удалось сохранить в БД, удаляем загруженный файл
os.Remove(filePath)
return "", fmt.Errorf("failed to update avatar in database: %v", err)
}
return avatarPath, nil
}
func (s *avatarService) DeleteAvatar(userID uint) error {
user, err := s.userRepo.FindByID(userID)
if err != nil {
return fmt.Errorf("user not found")
}
if user.Avatar == "" {
return nil // Аватара нет, ничего не делаем
}
// Удаляем файл
filePath := strings.TrimPrefix(user.Avatar, "/")
if _, err := os.Stat(filePath); err == nil {
if err := os.Remove(filePath); err != nil {
s.logger.Warn("Failed to delete avatar file", zap.Error(err))
}
}
// Очищаем поле в БД
return s.userRepo.UpdateAvatar(userID, "")
}
func (s *avatarService) GetAvatarPath(userID uint) (string, error) {
user, err := s.userRepo.FindByID(userID)
if err != nil {
return "", err
}
return user.Avatar, nil
}
func (s *avatarService) GetAvatarFile(filename string) ([]byte, string, error) {
// Валидация имени файла
if filename == "" || strings.Contains(filename, "..") || strings.Contains(filename, "/") {
return nil, "", fmt.Errorf("invalid filename")
}
// Проверяем допустимые расширения
allowedExts := map[string]string{
".png": "image/png",
".jpg": "image/jpeg",
".jpeg": "image/jpeg",
".gif": "image/gif",
".webp": "image/webp",
}
fileExt := strings.ToLower(filepath.Ext(filename))
contentType, exists := allowedExts[fileExt]
if !exists {
return nil, "", fmt.Errorf("unsupported file format")
}
// Формируем путь к файлу
filePath := filepath.Join("./uploads/avatars", filename)
// Проверяем существование файла
fileInfo, err := os.Stat(filePath)
if err != nil {
if os.IsNotExist(err) {
return nil, "", fmt.Errorf("avatar file not found")
}
return nil, "", fmt.Errorf("failed to access file: %v", err)
}
// Проверяем размер файла (максимум 10MB)
if fileInfo.Size() > 10*1024*1024 {
return nil, "", fmt.Errorf("file too large")
}
// Читаем файл
fileData, err := os.ReadFile(filePath)
if err != nil {
return nil, "", fmt.Errorf("failed to read file: %v", err)
}
return fileData, contentType, nil
}
func (s *avatarService) ServeAvatarFile(w io.Writer, filename string) (string, error) {
// Валидация имени файла
if filename == "" || strings.Contains(filename, "..") || strings.Contains(filename, "/") {
return "", fmt.Errorf("invalid filename")
}
// Проверяем допустимые расширения
allowedExts := map[string]string{
".png": "image/png",
".jpg": "image/jpeg",
".jpeg": "image/jpeg",
".gif": "image/gif",
".webp": "image/webp",
}
fileExt := strings.ToLower(filepath.Ext(filename))
contentType, exists := allowedExts[fileExt]
if !exists {
return "", fmt.Errorf("unsupported file format")
}
// Формируем путь к файлу
filePath := filepath.Join("./uploads/avatars", filename)
// Проверяем существование файла
fileInfo, err := os.Stat(filePath)
if err != nil {
if os.IsNotExist(err) {
return "", fmt.Errorf("avatar file not found")
}
return "", fmt.Errorf("failed to access file: %v", err)
}
// Проверяем размер файла
if fileInfo.Size() > 10*1024*1024 {
return "", fmt.Errorf("file too large")
}
// Открываем и копируем файл
file, err := os.Open(filePath)
if err != nil {
return "", fmt.Errorf("failed to open file: %v", err)
}
defer file.Close()
_, err = io.Copy(w, file)
if err != nil {
return "", fmt.Errorf("failed to serve file: %v", err)
}
return contentType, nil
}
@@ -0,0 +1,297 @@
// service/email_service.go
package service
import (
"fmt"
"time"
"api_bb/internal/models"
"api_bb/internal/repository"
"api_bb/pkg/email"
"api_bb/pkg/logger"
"github.com/google/uuid"
"go.uber.org/zap"
)
type EmailService struct {
emailRepo repository.EmailRepository
userRepo repository.UserRepository
emailSender email.Service
logger *zap.Logger
tokenExpiry time.Duration
passwordExpiry time.Duration
}
func NewEmailService(
emailRepo repository.EmailRepository,
userRepo repository.UserRepository,
emailSender email.Service,
) EmailService {
// Создаем логгер с контекстом для сервиса
serviceLogger := logger.Get().With(zap.String("service", "email"))
return EmailService{
emailRepo: emailRepo,
userRepo: userRepo,
emailSender: emailSender,
logger: serviceLogger,
tokenExpiry: 24 * time.Hour, // 24 часа для верификации
passwordExpiry: 1 * time.Hour, // 1 час для сброса пароля
}
}
func (s *EmailService) SendVerificationEmail(userID uint, email, userName string) error {
s.logger.Info("Sending verification email",
zap.Uint("user_id", userID),
zap.String("email", email),
)
token := uuid.New().String()
verification := &models.EmailVerification{
UserID: userID,
Token: token,
Email: email,
Type: "verification",
ExpiresAt: time.Now().Add(s.tokenExpiry),
}
if err := s.emailRepo.CreateVerificationToken(verification); err != nil {
s.logger.Error("Failed to create verification token",
zap.Uint("user_id", userID),
zap.String("email", email),
zap.Error(err),
)
return fmt.Errorf("failed to create verification token: %w", err)
}
if err := s.emailSender.SendVerificationEmail(email, userName, token); err != nil {
s.logger.Error("Failed to send verification email",
zap.Uint("user_id", userID),
zap.String("email", email),
zap.Error(err),
)
return fmt.Errorf("failed to send verification email: %w", err)
}
s.logger.Info("Verification email sent successfully",
zap.Uint("user_id", userID),
zap.String("email", email))
return nil
}
func (s *EmailService) VerifyEmail(token string) error {
s.logger.Info("Verifying email token",
zap.String("token", token),
)
verification, err := s.emailRepo.GetVerificationToken(token)
if err != nil {
s.logger.Error("Invalid or expired verification token",
zap.String("token", token),
zap.Error(err),
)
return fmt.Errorf("invalid or expired token: %w", err)
}
if verification.Type != "verification" {
s.logger.Error("Invalid token type for email verification",
zap.String("token", token),
zap.String("type", verification.Type),
)
return fmt.Errorf("invalid token type")
}
// Обновляем пользователя
if err := s.userRepo.MarkEmailAsVerified(verification.UserID); err != nil {
s.logger.Error("Failed to verify email in user repository",
zap.Uint("user_id", verification.UserID),
zap.String("email", verification.Email),
zap.Error(err),
)
return fmt.Errorf("failed to verify email: %w", err)
}
// Помечаем токен как использованный
if err := s.emailRepo.MarkTokenAsUsed(token); err != nil {
s.logger.Error("Failed to mark token as used",
zap.Error(err),
zap.String("token", token))
}
s.logger.Info("Email verified successfully",
zap.Uint("user_id", verification.UserID),
zap.String("email", verification.Email))
return nil
}
func (s *EmailService) SendPasswordResetEmail(email string) error {
s.logger.Info("Sending password reset email",
zap.String("email", email),
)
user, err := s.userRepo.FindByEmail(email)
if err != nil {
// Для безопасности не сообщаем, существует ли email
s.logger.Info("Password reset requested for non-existent email",
zap.String("email", email))
return nil
}
token := uuid.New().String()
resetRequest := &models.EmailVerification{
UserID: user.ID,
Token: token,
Email: email,
Type: "password_reset",
ExpiresAt: time.Now().Add(s.passwordExpiry),
}
if err := s.emailRepo.CreateVerificationToken(resetRequest); err != nil {
s.logger.Error("Failed to create password reset token",
zap.Uint("user_id", user.ID),
zap.String("email", email),
zap.Error(err),
)
return fmt.Errorf("failed to create password reset token: %w", err)
}
if err := s.emailSender.SendPasswordResetEmail(email, user.FirstName, token); err != nil {
s.logger.Error("Failed to send password reset email",
zap.Uint("user_id", user.ID),
zap.String("email", email),
zap.Error(err),
)
return fmt.Errorf("failed to send password reset email: %w", err)
}
s.logger.Info("Password reset email sent successfully",
zap.Uint("user_id", user.ID),
zap.String("email", email))
return nil
}
func (s *EmailService) ResetPassword(token, newPassword string) error {
s.logger.Info("Resetting password with token",
zap.String("token", token),
)
verification, err := s.emailRepo.GetVerificationToken(token)
if err != nil {
s.logger.Error("Invalid or expired password reset token",
zap.String("token", token),
zap.Error(err),
)
return fmt.Errorf("invalid or expired token: %w", err)
}
if verification.Type != "password_reset" {
s.logger.Error("Invalid token type for password reset",
zap.String("token", token),
zap.String("type", verification.Type),
)
return fmt.Errorf("invalid token type")
}
// Обновляем пароль пользователя
if err := s.userRepo.UpdatePassword(verification.UserID, newPassword); err != nil {
s.logger.Error("Failed to update password",
zap.Uint("user_id", verification.UserID),
zap.Error(err),
)
return fmt.Errorf("failed to update password: %w", err)
}
// Помечаем токен как использованный
if err := s.emailRepo.MarkTokenAsUsed(token); err != nil {
s.logger.Error("Failed to mark token as used",
zap.Error(err),
zap.String("token", token))
}
s.logger.Info("Password reset successfully",
zap.Uint("user_id", verification.UserID))
return nil
}
func (s *EmailService) SendNewsletterToSubscribers(subject, content string) error {
s.logger.Info("Sending newsletter to subscribers",
zap.String("subject", subject),
)
subscribers, err := s.emailRepo.GetUsersWithNewsletter()
if err != nil {
s.logger.Error("Failed to get subscribers",
zap.Error(err),
)
return fmt.Errorf("failed to get subscribers: %w", err)
}
s.logger.Debug("Found subscribers for newsletter",
zap.Int("count", len(subscribers)),
)
var errors []error
for _, user := range subscribers {
if err := s.emailSender.SendNewsletterEmail(user.Email, user.FirstName, subject, content); err != nil {
s.logger.Error("Failed to send newsletter to user",
zap.Uint("user_id", user.ID),
zap.String("email", user.Email),
zap.Error(err))
errors = append(errors, err)
continue
}
s.logger.Debug("Newsletter sent to user",
zap.Uint("user_id", user.ID),
zap.String("email", user.Email))
}
if len(errors) > 0 {
s.logger.Error("Failed to send newsletter to some users",
zap.Int("failed_count", len(errors)),
zap.Int("total_subscribers", len(subscribers)),
)
return fmt.Errorf("failed to send newsletter to %d users", len(errors))
}
s.logger.Info("Newsletter sent to all subscribers",
zap.Int("total_subscribers", len(subscribers)))
return nil
}
func (s *EmailService) CleanupExpiredTokens() error {
s.logger.Info("Cleaning up expired tokens")
if err := s.emailRepo.DeleteExpiredTokens(); err != nil {
s.logger.Error("Failed to cleanup expired tokens",
zap.Error(err),
)
return fmt.Errorf("failed to cleanup expired tokens: %w", err)
}
s.logger.Info("Expired tokens cleaned up successfully")
return nil
}
// GetUserByID возвращает пользователя по ID
func (s *EmailService) GetUserByID(userID uint) (*models.User, error) {
s.logger.Info("Getting user by ID",
zap.Uint("user_id", userID),
)
user, err := s.userRepo.GetUserByID(userID)
if err != nil {
s.logger.Error("Failed to get user by ID",
zap.Uint("user_id", userID),
zap.Error(err),
)
return nil, fmt.Errorf("failed to get user: %w", err)
}
s.logger.Debug("User retrieved successfully",
zap.Uint("user_id", userID),
zap.String("email", user.Email),
)
return user, nil
}
@@ -0,0 +1,380 @@
// service/event_registration_service.go
package service
import (
"api_bb/internal/models"
"api_bb/internal/repository"
"api_bb/pkg/logger"
"fmt"
"go.uber.org/zap"
)
type EventRegistrationService interface {
RegisterForEvent(registration *models.EventRegistration) error
GetRegistrationByID(id uint) (*models.EventRegistration, error)
GetRegistrationsByEventID(eventID uint) ([]models.EventRegistration, error)
GetRegistrationsByUserID(userID uint) ([]models.EventRegistration, error)
GetRegistrationByEventAndUser(eventID, userID uint) (*models.EventRegistration, error)
UpdateRegistration(registration *models.EventRegistration) error
CancelRegistration(id uint) error
UpdateRegistrationStatus(registrationID uint, status string) error
UpdateResultTime(registrationID uint, resultTime string) error
CheckEventAvailability(eventID uint) (bool, error)
}
type eventRegistrationService struct {
registrationRepo repository.EventRegistrationRepository
eventRepo repository.EventRepository
logger logger.LoggerInterface
}
func NewEventRegistrationService(
registrationRepo repository.EventRegistrationRepository,
eventRepo repository.EventRepository,
log logger.LoggerInterface,
) EventRegistrationService {
serviceLogger := log.With(zap.String("service", "event_registration"))
return &eventRegistrationService{
registrationRepo: registrationRepo,
eventRepo: eventRepo,
logger: serviceLogger,
}
}
// RegisterForEvent регистрирует пользователя на событие
func (s *eventRegistrationService) RegisterForEvent(registration *models.EventRegistration) error {
s.logger.Info("Registering user for event",
zap.Uint("user_id", registration.UserID),
zap.Uint("event_id", registration.EventID),
)
// Проверяем существование события
event, err := s.eventRepo.FindByID(registration.EventID)
if err != nil {
s.logger.Warn("Event not found for registration",
zap.Uint("event_id", registration.EventID),
zap.Error(err),
)
return fmt.Errorf("event not found")
}
// Проверяем, открыта ли регистрация
if !event.RegistrationOpen {
s.logger.Warn("Registration is closed for event",
zap.Uint("event_id", registration.EventID),
zap.String("event_title", event.Title),
)
return fmt.Errorf("registration is closed for this event")
}
// Проверяем, не зарегистрирован ли пользователь уже
existingRegistration, err := s.registrationRepo.FindByEventAndUser(registration.EventID, registration.UserID)
if err == nil && existingRegistration != nil {
s.logger.Warn("User already registered for event",
zap.Uint("user_id", registration.UserID),
zap.Uint("event_id", registration.EventID),
)
return fmt.Errorf("user already registered for this event")
}
// Проверяем доступность мест
available, err := s.CheckEventAvailability(registration.EventID)
if err != nil {
s.logger.Error("Failed to check event availability",
zap.Uint("event_id", registration.EventID),
zap.Error(err),
)
return fmt.Errorf("failed to check event availability: %w", err)
}
if !available {
s.logger.Warn("Event is full",
zap.Uint("event_id", registration.EventID),
zap.String("event_title", event.Title),
)
return fmt.Errorf("event is full")
}
// Создаем регистрацию
if err := s.registrationRepo.Create(registration); err != nil {
s.logger.Error("Failed to create registration",
zap.Uint("user_id", registration.UserID),
zap.Uint("event_id", registration.EventID),
zap.Error(err),
)
return fmt.Errorf("failed to register for event: %w", err)
}
// Обновляем счетчик участников
if err := s.eventRepo.UpdateParticipantsCount(registration.EventID, event.ParticipantsCount+1); err != nil {
s.logger.Error("Failed to update participants count",
zap.Uint("event_id", registration.EventID),
zap.Error(err),
)
// Не прерываем выполнение, только логируем ошибку
}
s.logger.Info("User registered for event successfully",
zap.Uint("user_id", registration.UserID),
zap.Uint("event_id", registration.EventID),
zap.String("status", registration.Status),
)
return nil
}
// GetRegistrationByID возвращает регистрацию по ID
func (s *eventRegistrationService) GetRegistrationByID(id uint) (*models.EventRegistration, error) {
s.logger.Debug("Getting registration by ID", zap.Uint("registration_id", id))
registration, err := s.registrationRepo.FindByID(id)
if err != nil {
s.logger.Warn("Registration not found",
zap.Uint("registration_id", id),
zap.Error(err),
)
return nil, fmt.Errorf("registration not found: %w", err)
}
s.logger.Debug("Registration retrieved successfully",
zap.Uint("registration_id", id),
zap.Uint("user_id", registration.UserID),
zap.Uint("event_id", registration.EventID),
)
return registration, nil
}
// GetRegistrationsByEventID возвращает все регистрации на событие
func (s *eventRegistrationService) GetRegistrationsByEventID(eventID uint) ([]models.EventRegistration, error) {
s.logger.Debug("Getting registrations by event ID", zap.Uint("event_id", eventID))
registrations, err := s.registrationRepo.FindByEventID(eventID)
if err != nil {
s.logger.Error("Failed to get registrations by event ID",
zap.Uint("event_id", eventID),
zap.Error(err),
)
return nil, fmt.Errorf("failed to get registrations: %w", err)
}
s.logger.Debug("Registrations by event retrieved successfully",
zap.Uint("event_id", eventID),
zap.Int("count", len(registrations)),
)
return registrations, nil
}
// GetRegistrationsByUserID возвращает все регистрации пользователя
func (s *eventRegistrationService) GetRegistrationsByUserID(userID uint) ([]models.EventRegistration, error) {
s.logger.Debug("Getting registrations by user ID", zap.Uint("user_id", userID))
registrations, err := s.registrationRepo.FindByUserID(userID)
if err != nil {
s.logger.Error("Failed to get registrations by user ID",
zap.Uint("user_id", userID),
zap.Error(err),
)
return nil, fmt.Errorf("failed to get user registrations: %w", err)
}
s.logger.Debug("User registrations retrieved successfully",
zap.Uint("user_id", userID),
zap.Int("count", len(registrations)),
)
return registrations, nil
}
// GetRegistrationByEventAndUser возвращает регистрацию по событию и пользователю
func (s *eventRegistrationService) GetRegistrationByEventAndUser(eventID, userID uint) (*models.EventRegistration, error) {
s.logger.Debug("Getting registration by event and user",
zap.Uint("event_id", eventID),
zap.Uint("user_id", userID),
)
registration, err := s.registrationRepo.FindByEventAndUser(eventID, userID)
if err != nil {
s.logger.Debug("Registration not found for event and user",
zap.Uint("event_id", eventID),
zap.Uint("user_id", userID),
zap.Error(err),
)
return nil, fmt.Errorf("registration not found: %w", err)
}
s.logger.Debug("Registration by event and user retrieved successfully",
zap.Uint("event_id", eventID),
zap.Uint("user_id", userID),
)
return registration, nil
}
// UpdateRegistration обновляет регистрацию
func (s *eventRegistrationService) UpdateRegistration(registration *models.EventRegistration) error {
s.logger.Info("Updating registration",
zap.Uint("registration_id", registration.ID),
zap.Uint("user_id", registration.UserID),
zap.Uint("event_id", registration.EventID),
)
// Проверяем существование регистрации
existingRegistration, err := s.registrationRepo.FindByID(registration.ID)
if err != nil {
s.logger.Warn("Registration not found for update",
zap.Uint("registration_id", registration.ID),
zap.Error(err),
)
return fmt.Errorf("registration not found")
}
// Сохраняем неизменяемые поля
registration.CreatedAt = existingRegistration.CreatedAt
if err := s.registrationRepo.Update(registration); err != nil {
s.logger.Error("Failed to update registration",
zap.Uint("registration_id", registration.ID),
zap.Error(err),
)
return fmt.Errorf("failed to update registration: %w", err)
}
s.logger.Info("Registration updated successfully",
zap.Uint("registration_id", registration.ID),
)
return nil
}
// CancelRegistration отменяет регистрацию
func (s *eventRegistrationService) CancelRegistration(id uint) error {
s.logger.Info("Canceling registration", zap.Uint("registration_id", id))
// Получаем регистрацию для получения event_id
registration, err := s.registrationRepo.FindByID(id)
if err != nil {
s.logger.Warn("Registration not found for cancellation",
zap.Uint("registration_id", id),
zap.Error(err),
)
return fmt.Errorf("registration not found")
}
if err := s.registrationRepo.Delete(id); err != nil {
s.logger.Error("Failed to cancel registration",
zap.Uint("registration_id", id),
zap.Error(err),
)
return fmt.Errorf("failed to cancel registration: %w", err)
}
// Обновляем счетчик участников
if err := s.eventRepo.UpdateParticipantsCount(registration.EventID, registration.Event.ParticipantsCount-1); err != nil {
s.logger.Error("Failed to update participants count after cancellation",
zap.Uint("event_id", registration.EventID),
zap.Error(err),
)
// Не прерываем выполнение, только логируем ошибку
}
s.logger.Info("Registration canceled successfully",
zap.Uint("registration_id", id),
zap.Uint("event_id", registration.EventID),
)
return nil
}
// UpdateRegistrationStatus обновляет статус регистрации
func (s *eventRegistrationService) UpdateRegistrationStatus(registrationID uint, status string) error {
s.logger.Info("Updating registration status",
zap.Uint("registration_id", registrationID),
zap.String("status", status),
)
validStatuses := []string{"pending", "confirmed", "cancelled", "completed"}
if !contains(validStatuses, status) {
s.logger.Warn("Invalid registration status",
zap.String("status", status),
zap.Strings("valid_statuses", validStatuses),
)
return fmt.Errorf("invalid status: %s", status)
}
if err := s.registrationRepo.UpdateStatus(registrationID, status); err != nil {
s.logger.Error("Failed to update registration status",
zap.Uint("registration_id", registrationID),
zap.String("status", status),
zap.Error(err),
)
return fmt.Errorf("failed to update registration status: %w", err)
}
s.logger.Info("Registration status updated successfully",
zap.Uint("registration_id", registrationID),
zap.String("status", status),
)
return nil
}
// UpdateResultTime обновляет результат забега
func (s *eventRegistrationService) UpdateResultTime(registrationID uint, resultTime string) error {
s.logger.Info("Updating result time",
zap.Uint("registration_id", registrationID),
zap.String("result_time", resultTime),
)
if err := s.registrationRepo.UpdateResultTime(registrationID, resultTime); err != nil {
s.logger.Error("Failed to update result time",
zap.Uint("registration_id", registrationID),
zap.String("result_time", resultTime),
zap.Error(err),
)
return fmt.Errorf("failed to update result time: %w", err)
}
s.logger.Info("Result time updated successfully",
zap.Uint("registration_id", registrationID),
zap.String("result_time", resultTime),
)
return nil
}
// CheckEventAvailability проверяет доступность мест на событии
func (s *eventRegistrationService) CheckEventAvailability(eventID uint) (bool, error) {
s.logger.Debug("Checking event availability", zap.Uint("event_id", eventID))
event, err := s.eventRepo.FindByID(eventID)
if err != nil {
return false, fmt.Errorf("event not found: %w", err)
}
// Если максимальное количество участников не установлено, считаем доступным
if event.MaxParticipants == 0 {
return true, nil
}
// Получаем текущее количество подтвержденных регистраций
currentCount, err := s.registrationRepo.CountByEventID(eventID)
if err != nil {
return false, fmt.Errorf("failed to count registrations: %w", err)
}
available := int(currentCount) < event.MaxParticipants
s.logger.Debug("Event availability check completed",
zap.Uint("event_id", eventID),
zap.Int64("current_count", currentCount),
zap.Int("max_participants", event.MaxParticipants),
zap.Bool("available", available),
)
return available, nil
}
// contains проверяет наличие строки в слайсе
func contains(slice []string, item string) bool {
for _, s := range slice {
if s == item {
return true
}
}
return false
}
@@ -0,0 +1,280 @@
// service/event_service.go
package service
import (
"api_bb/internal/models"
"api_bb/internal/repository"
"api_bb/pkg/logger"
"fmt"
"time"
"go.uber.org/zap"
)
type EventService interface {
CreateEvent(event *models.Event) error
GetEventByID(id uint) (*models.Event, error)
GetAllEvents() ([]models.Event, error)
UpdateEvent(event *models.Event) error
DeleteEvent(id uint) error
GetEventsByType(eventType models.EventType) ([]models.Event, error)
GetUpcomingEvents() ([]models.Event, error)
GetEventsByDateRange(startDate, endDate time.Time) ([]models.Event, error)
UpdateParticipantsCount(eventID uint) error
ToggleRegistrationStatus(eventID uint, registrationOpen bool) error
}
type eventService struct {
eventRepo repository.EventRepository
registrationRepo repository.EventRegistrationRepository
logger logger.LoggerInterface
}
func NewEventService(
eventRepo repository.EventRepository,
registrationRepo repository.EventRegistrationRepository,
log logger.LoggerInterface,
) EventService {
serviceLogger := log.With(zap.String("service", "event"))
return &eventService{
eventRepo: eventRepo,
registrationRepo: registrationRepo,
logger: serviceLogger,
}
}
// CreateEvent создает новое событие
func (s *eventService) CreateEvent(event *models.Event) error {
s.logger.Info("Creating new event",
zap.String("title", event.Title),
zap.String("type", string(event.Type)),
zap.Time("date", event.Date),
)
if err := s.eventRepo.Create(event); err != nil {
s.logger.Error("Failed to create event",
zap.String("title", event.Title),
zap.Error(err),
)
return fmt.Errorf("failed to create event: %w", err)
}
s.logger.Info("Event created successfully",
zap.Uint("event_id", event.ID),
zap.String("title", event.Title),
)
return nil
}
// GetEventByID возвращает событие по ID
func (s *eventService) GetEventByID(id uint) (*models.Event, error) {
s.logger.Debug("Getting event by ID", zap.Uint("event_id", id))
event, err := s.eventRepo.FindByID(id)
if err != nil {
s.logger.Warn("Event not found",
zap.Uint("event_id", id),
zap.Error(err),
)
return nil, fmt.Errorf("event not found: %w", err)
}
s.logger.Debug("Event retrieved successfully",
zap.Uint("event_id", id),
zap.String("title", event.Title),
)
return event, nil
}
// GetAllEvents возвращает все события
func (s *eventService) GetAllEvents() ([]models.Event, error) {
s.logger.Debug("Getting all events")
events, err := s.eventRepo.FindAll()
if err != nil {
s.logger.Error("Failed to get events", zap.Error(err))
return nil, fmt.Errorf("failed to get events: %w", err)
}
s.logger.Debug("Events retrieved successfully",
zap.Int("count", len(events)),
)
return events, nil
}
// UpdateEvent обновляет событие
func (s *eventService) UpdateEvent(event *models.Event) error {
s.logger.Info("Updating event",
zap.Uint("event_id", event.ID),
zap.String("title", event.Title),
)
// Проверяем существование события
existingEvent, err := s.eventRepo.FindByID(event.ID)
if err != nil {
s.logger.Warn("Event not found for update",
zap.Uint("event_id", event.ID),
zap.Error(err),
)
return fmt.Errorf("event not found")
}
// Сохраняем неизменяемые поля
event.CreatedAt = existingEvent.CreatedAt
event.UpdatedAt = time.Now()
if err := s.eventRepo.Update(event); err != nil {
s.logger.Error("Failed to update event",
zap.Uint("event_id", event.ID),
zap.Error(err),
)
return fmt.Errorf("failed to update event: %w", err)
}
s.logger.Info("Event updated successfully",
zap.Uint("event_id", event.ID),
)
return nil
}
// DeleteEvent удаляет событие
func (s *eventService) DeleteEvent(id uint) error {
s.logger.Info("Deleting event", zap.Uint("event_id", id))
// Проверяем существование события
_, err := s.eventRepo.FindByID(id)
if err != nil {
s.logger.Warn("Event not found for deletion",
zap.Uint("event_id", id),
zap.Error(err),
)
return fmt.Errorf("event not found")
}
if err := s.eventRepo.Delete(id); err != nil {
s.logger.Error("Failed to delete event",
zap.Uint("event_id", id),
zap.Error(err),
)
return fmt.Errorf("failed to delete event: %w", err)
}
s.logger.Info("Event deleted successfully",
zap.Uint("event_id", id),
)
return nil
}
// GetEventsByType возвращает события по типу
func (s *eventService) GetEventsByType(eventType models.EventType) ([]models.Event, error) {
s.logger.Debug("Getting events by type", zap.String("type", string(eventType)))
events, err := s.eventRepo.FindByType(eventType)
if err != nil {
s.logger.Error("Failed to get events by type",
zap.String("type", string(eventType)),
zap.Error(err),
)
return nil, fmt.Errorf("failed to get events by type: %w", err)
}
s.logger.Debug("Events by type retrieved successfully",
zap.String("type", string(eventType)),
zap.Int("count", len(events)),
)
return events, nil
}
// GetUpcomingEvents возвращает предстоящие события
func (s *eventService) GetUpcomingEvents() ([]models.Event, error) {
s.logger.Debug("Getting upcoming events")
events, err := s.eventRepo.FindUpcoming()
if err != nil {
s.logger.Error("Failed to get upcoming events", zap.Error(err))
return nil, fmt.Errorf("failed to get upcoming events: %w", err)
}
s.logger.Debug("Upcoming events retrieved successfully",
zap.Int("count", len(events)),
)
return events, nil
}
// GetEventsByDateRange возвращает события в диапазоне дат
func (s *eventService) GetEventsByDateRange(startDate, endDate time.Time) ([]models.Event, error) {
s.logger.Debug("Getting events by date range",
zap.Time("start_date", startDate),
zap.Time("end_date", endDate),
)
events, err := s.eventRepo.FindByDateRange(startDate, endDate)
if err != nil {
s.logger.Error("Failed to get events by date range",
zap.Time("start_date", startDate),
zap.Time("end_date", endDate),
zap.Error(err),
)
return nil, fmt.Errorf("failed to get events by date range: %w", err)
}
s.logger.Debug("Events by date range retrieved successfully",
zap.Time("start_date", startDate),
zap.Time("end_date", endDate),
zap.Int("count", len(events)),
)
return events, nil
}
// UpdateParticipantsCount обновляет количество участников события
func (s *eventService) UpdateParticipantsCount(eventID uint) error {
s.logger.Debug("Updating participants count", zap.Uint("event_id", eventID))
count, err := s.registrationRepo.CountByEventID(eventID)
if err != nil {
s.logger.Error("Failed to count event registrations",
zap.Uint("event_id", eventID),
zap.Error(err),
)
return fmt.Errorf("failed to count registrations: %w", err)
}
if err := s.eventRepo.UpdateParticipantsCount(eventID, int(count)); err != nil {
s.logger.Error("Failed to update participants count",
zap.Uint("event_id", eventID),
zap.Int64("count", count),
zap.Error(err),
)
return fmt.Errorf("failed to update participants count: %w", err)
}
s.logger.Debug("Participants count updated successfully",
zap.Uint("event_id", eventID),
zap.Int64("count", count),
)
return nil
}
// ToggleRegistrationStatus переключает статус регистрации на событие
func (s *eventService) ToggleRegistrationStatus(eventID uint, registrationOpen bool) error {
s.logger.Info("Toggling registration status",
zap.Uint("event_id", eventID),
zap.Bool("registration_open", registrationOpen),
)
if err := s.eventRepo.UpdateRegistrationStatus(eventID, registrationOpen); err != nil {
s.logger.Error("Failed to toggle registration status",
zap.Uint("event_id", eventID),
zap.Bool("registration_open", registrationOpen),
zap.Error(err),
)
return fmt.Errorf("failed to toggle registration status: %w", err)
}
s.logger.Info("Registration status updated successfully",
zap.Uint("event_id", eventID),
zap.Bool("registration_open", registrationOpen),
)
return nil
}
@@ -0,0 +1,61 @@
// service/jwt_service.go
package service
import (
"errors"
"fmt"
"time"
"github.com/golang-jwt/jwt/v5"
)
type JWTService interface {
GenerateToken(userID uint, email string) (string, error)
ValidateToken(tokenString string) (*jwt.Token, error)
ExtractUserID(token *jwt.Token) (uint, error)
}
type jwtService struct {
secretKey string
}
func NewJWTService(secretKey string) JWTService {
return &jwtService{secretKey: secretKey}
}
type Claims struct {
UserID uint `json:"user_id"`
Email string `json:"email"`
jwt.RegisteredClaims
}
func (j *jwtService) GenerateToken(userID uint, email string) (string, error) {
claims := &Claims{
UserID: userID,
Email: email,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)),
IssuedAt: jwt.NewNumericDate(time.Now()),
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString([]byte(j.secretKey))
}
func (j *jwtService) ValidateToken(tokenString string) (*jwt.Token, error) {
return jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
return []byte(j.secretKey), nil
})
}
func (j *jwtService) ExtractUserID(token *jwt.Token) (uint, error) {
claims, ok := token.Claims.(*Claims)
if !ok {
return 0, errors.New("invalid token claims")
}
return claims.UserID, nil
}

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