├── backend
├── src
│ ├── rewards
│ │ ├── rewards.controller.spec.ts
│ │ ├── dto
│ │ │ └── claim-reward.dto.ts
│ │ └── rewards.module.ts
│ ├── session
│ │ ├── enum
│ │ │ └── activityType.enum.ts
│ │ ├── session.module.ts
│ │ ├── session.entity.ts
│ │ └── session.controller.ts
│ ├── puzzle-draft
│ │ ├── dto
│ │ │ ├── publish-draft.dto.ts
│ │ │ ├── update-draft.dto.ts
│ │ │ └── create-draft.dto.ts
│ │ ├── puzzle-draft.module.ts
│ │ └── entities
│ │ │ └── draft-puzzle.entity.ts
│ ├── api-key
│ │ ├── entities
│ │ │ └── api-key.entity.ts
│ │ ├── dto
│ │ │ ├── create-api-key.dto.ts
│ │ │ └── update-api-key.dto.ts
│ │ ├── api-key.module.ts
│ │ ├── api-key.service.spec.ts
│ │ ├── api-key.controller.spec.ts
│ │ └── api-key.guard.ts
│ ├── wallet
│ │ ├── wallet.entity.ts
│ │ ├── entities
│ │ │ └── wallet.entity.ts
│ │ ├── wallet.module.ts
│ │ └── wallet.service.ts
│ ├── analytics
│ │ ├── entities
│ │ │ └── analytics.entity.ts
│ │ ├── dto
│ │ │ ├── create-analytics.dto.ts
│ │ │ └── update-analytics.dto.ts
│ │ ├── analytics.module.ts
│ │ ├── analytics.service.spec.ts
│ │ └── analytics.controller.spec.ts
│ ├── progress
│ │ ├── dto
│ │ │ ├── create-progress.dto.ts
│ │ │ ├── update-progress.dto.ts
│ │ │ └── progress-response.dto.ts
│ │ ├── progress.module.ts
│ │ ├── entities
│ │ │ └── progress.entity.ts
│ │ └── progress.controller.ts
│ ├── reward-shop
│ │ ├── entities
│ │ │ └── reward-shop.entity.ts
│ │ ├── dto
│ │ │ ├── create-reward-shop.dto.ts
│ │ │ └── update-reward-shop.dto.ts
│ │ ├── reward-shop.module.ts
│ │ ├── reward-shop.service.spec.ts
│ │ └── reward-shop.controller.spec.ts
│ ├── achievements
│ │ ├── dto
│ │ │ ├── create-achievement.dto.ts
│ │ │ ├── update-achievement.dto.ts
│ │ │ └── player-achievements.dto.ts
│ │ ├── achievements.service.spec.ts
│ │ ├── achievements.module.ts
│ │ ├── achievements.controller.spec.ts
│ │ └── entities
│ │ │ ├── player-achievements.entity.ts
│ │ │ └── achievement.entity.ts
│ ├── puzzle-comment
│ │ ├── entities
│ │ │ └── puzzle-comment.entity.ts
│ │ ├── dto
│ │ │ ├── create-puzzle-comment.dto.ts
│ │ │ └── update-puzzle-comment.dto.ts
│ │ ├── puzzle-comment.module.ts
│ │ ├── puzzle-comment.service.spec.ts
│ │ └── puzzle-comment.controller.spec.ts
│ ├── user-inventory
│ │ ├── entities
│ │ │ ├── user-inventory.entity.ts
│ │ │ ├── badge.ts
│ │ │ ├── nft.ts
│ │ │ └── user.ts
│ │ ├── dto
│ │ │ ├── create-user-inventory.dto.ts
│ │ │ ├── update-user-inventory.dto.ts
│ │ │ └── add-inventory-item.ts
│ │ └── user-inventory.module.ts
│ ├── user-report-card
│ │ ├── dto
│ │ │ ├── create-user-report-card.dto.ts
│ │ │ └── update-user-report-card.dto.ts
│ │ ├── user-report-card.module.ts
│ │ ├── user-report-card.service.spec.ts
│ │ ├── user-report-card.controller.spec.ts
│ │ └── entities
│ │ │ └── user-report-card.entity.ts
│ ├── common
│ │ ├── enums
│ │ │ └── roles.enum.ts
│ │ ├── decorators
│ │ │ └── roles.decorator.ts
│ │ └── gaurds
│ │ │ └── roles.gaurds.ts
│ ├── auth
│ │ ├── enums
│ │ │ └── auth-type.enum.ts
│ │ ├── decorators
│ │ │ ├── auth-decorator.ts
│ │ │ └── current-user.decorator.ts
│ │ ├── dto
│ │ │ ├── login.dto.ts
│ │ │ └── auth-response.dto.ts
│ │ ├── exceptions
│ │ │ └── auth.exceptions.ts
│ │ ├── middleware
│ │ │ └── auth.middleware.ts
│ │ └── strategies
│ │ │ └── jwt.strategy.ts
│ ├── admin
│ │ ├── admin-role.enum.ts
│ │ ├── guards
│ │ │ ├── jwt-auth.guard.ts
│ │ │ └── roles.guard.ts
│ │ ├── dto
│ │ │ ├── login-admin.dto.ts
│ │ │ └── create-admin.dto.ts
│ │ ├── roles.decorator.ts
│ │ ├── admin.entity.ts
│ │ ├── strategies
│ │ │ ├── local.strategy.ts
│ │ │ └── jwt.strategy.ts
│ │ └── admin.module.ts
│ ├── badge
│ │ ├── dto
│ │ │ └── assign-badge.dto.ts
│ │ ├── badge.module.ts
│ │ ├── entities
│ │ │ ├── user-badge.entity.ts
│ │ │ └── badge.entity.ts
│ │ └── badge.controller.ts
│ ├── rate-limiter
│ │ ├── rate-limit.interface.ts
│ │ ├── rate-limit.decorator.ts
│ │ ├── rate-limiter.module.ts
│ │ └── rate-limiter.service.ts
│ ├── hint
│ │ ├── create-hint.dto.ts
│ │ ├── hint.entity.ts
│ │ ├── hint.module.ts
│ │ ├── hint.service.ts
│ │ └── hint.controller.ts
│ ├── gameMechanics
│ │ ├── dto
│ │ │ ├── request-hint.dto.ts
│ │ │ ├── update-challenge.dto.ts
│ │ │ └── submit-puzzle.dto.ts
│ │ ├── guards
│ │ │ └── admin.guard.ts
│ │ └── entities
│ │ │ ├── hint-usage.entity.ts
│ │ │ ├── puzzle-submission.entity.ts
│ │ │ └── challenge-completion.entity.ts
│ ├── quiz
│ │ ├── enums
│ │ │ └── question-type.enum.ts
│ │ ├── dto
│ │ │ ├── update-quiz.dto.ts
│ │ │ ├── submit-quiz.dto.ts
│ │ │ └── quiz-result.dto.ts
│ │ ├── quiz.service.spec.ts
│ │ ├── quiz.module.ts
│ │ ├── quiz.controller.spec.ts
│ │ ├── interfaces
│ │ │ └── quiz.interface.ts
│ │ └── entities
│ │ │ ├── quiz-option.entity.ts
│ │ │ └── quiz.entity.ts
│ ├── app.service.ts
│ ├── geostats
│ │ ├── dto
│ │ │ └── create-geostat.dto.ts
│ │ ├── entities
│ │ │ └── geostat.entity.ts
│ │ ├── geostats.controller.ts
│ │ └── geostats.module.ts
│ ├── daily-reward
│ │ ├── dto
│ │ │ └── daily-checkin.dto.ts
│ │ ├── entities
│ │ │ └── daily-reward-log.entity.ts
│ │ ├── daily-reward.module.ts
│ │ └── daily-reward.controller.ts
│ ├── nft-marketplace-stub
│ │ ├── entities
│ │ │ └── nft-item.entity.ts
│ │ ├── nft-marketplace-stub.module.ts
│ │ └── nft-marketplace-stub.controller.ts
│ ├── maintenance-mode
│ │ ├── decorators
│ │ │ ├── admin-only.decorator.ts
│ │ │ └── maintenance-exempt.decorator.ts
│ │ └── guards
│ │ │ └── admin.guard.ts
│ ├── puzzle
│ │ ├── dto
│ │ │ ├── update-puzzle.dto.ts
│ │ │ └── create-puzzle.dto.ts
│ │ ├── puzzle.module.ts
│ │ └── puzzle.entity.ts
│ ├── reports
│ │ ├── dto
│ │ │ ├── update-report.dto.ts
│ │ │ └── create-report.dto.ts
│ │ ├── reports.module.ts
│ │ ├── entities
│ │ │ └── report.entity.ts
│ │ ├── reports.controller.spec.ts
│ │ └── reports.service.ts
│ ├── content
│ │ ├── dto
│ │ │ ├── update-content.dto.ts
│ │ │ └── create-content.dto.ts
│ │ ├── content.module.ts
│ │ └── content.entity.ts
│ ├── promo-code
│ │ ├── dto
│ │ │ └── redeem-promo-code.dto.ts
│ │ ├── entities
│ │ │ ├── promo-code.entities.ts
│ │ │ ├── promo-code-redemption.entity.ts
│ │ │ └── promo-code.module.ts
│ │ └── promo-code.controller.ts
│ ├── referral
│ │ ├── dto
│ │ │ ├── referral-stats.dto.ts
│ │ │ ├── create-referral-code.dto.ts
│ │ │ └── create-invite.dto.ts
│ │ └── referral.module.ts
│ ├── user-ranking
│ │ ├── dto
│ │ │ ├── update-user-ranking.dto.ts
│ │ │ └── create-user-ranking.dto.ts
│ │ ├── user-ranking.module.ts
│ │ ├── user-ranking.service.spec.ts
│ │ ├── entities
│ │ │ └── user-ranking.entity.ts
│ │ ├── user-ranking.controller.spec.ts
│ │ └── user-ranking.controller.ts
│ ├── puzzle-translation
│ │ ├── dto
│ │ │ ├── update-translation.dto.ts
│ │ │ └── create-translation.dto.ts
│ │ ├── puzzle-translation.module.ts
│ │ └── entities
│ │ │ └── puzzle-translation.entity.ts
│ ├── puzzle-access-log
│ │ ├── dto
│ │ │ └── log-access.dto.ts
│ │ ├── puzzle-access-log.module.ts
│ │ ├── entities
│ │ │ └── puzzle-access-log.entity.ts
│ │ └── puzzle-access-log.controller.ts
│ ├── puzzle-dependency
│ │ ├── dto
│ │ │ ├── update-puzzle-dependency.dto.ts
│ │ │ ├── mark-completion.dto.ts
│ │ │ ├── check-eligibility.dto.ts
│ │ │ └── create-puzzle-dependency.dto.ts
│ │ ├── interfaces
│ │ │ └── eligibility-result.interface.ts
│ │ ├── puzzle-dependency.service.spec.ts
│ │ ├── entities
│ │ │ ├── puzzle-completion.entity.ts
│ │ │ └── puzzle-dependency.entity.ts
│ │ ├── puzzle-dependency.module.ts
│ │ └── puzzle-dependency.controller.spec.ts
│ ├── user-reaction
│ │ ├── dto
│ │ │ ├── update-reaction.dto.ts
│ │ │ ├── reaction-aggregation.dto.ts
│ │ │ └── create-reaction.dto.ts
│ │ ├── user-reaction.module.ts
│ │ └── entities
│ │ │ └── reaction.entity.ts
│ ├── user
│ │ ├── dto
│ │ │ ├── link-wallet.dto.ts
│ │ │ ├── update-user-profile.dto.ts
│ │ │ └── create-user.dto.ts
│ │ ├── user.module.ts
│ │ └── entities
│ │ │ └── user.entity.ts
│ ├── content-rating
│ │ ├── dto
│ │ │ └── create-rating.dto.ts
│ │ ├── content-rating.module.ts
│ │ ├── content-rating.service.spec.ts
│ │ ├── content-rating.controller.spec.ts
│ │ ├── entities
│ │ │ └── content-rating.entity.ts
│ │ └── content-rating.controller.ts
│ ├── in-app-notifications
│ │ ├── dto
│ │ │ ├── mark-read.dto.ts
│ │ │ ├── system-notification.dto.ts
│ │ │ ├── notification-response.dto.ts
│ │ │ └── create-notification.dto.ts
│ │ └── in-app-notifications.module.ts
│ ├── puzzle-fork
│ │ ├── dto
│ │ │ └── create-fork.dto.ts
│ │ ├── puzzle-fork.module.ts
│ │ ├── puzzle-fork.controller.ts
│ │ └── entities
│ │ │ └── forked-puzzle.entity.ts
│ ├── puzzle-versioning
│ │ ├── dto
│ │ │ └── create-puzzle-version.dto.ts
│ │ ├── puzzle-versioning.module.ts
│ │ ├── entities
│ │ │ └── puzzle-version.entity.ts
│ │ └── puzzle-versioning.controller.ts
│ ├── audit-log
│ │ ├── Dto
│ │ │ └── filter-audit-log.dto.ts
│ │ ├── entities
│ │ │ └── audit-log.entity.ts
│ │ ├── audit-log.module.ts
│ │ ├── audit-log.controller.ts
│ │ ├── interceptor
│ │ │ └── audit-log.interceptor.ts
│ │ └── audit-log.service.ts
│ ├── user-activity-log
│ │ ├── dto
│ │ │ └── filter-activity.dto.ts
│ │ ├── user-activity-log.module.ts
│ │ ├── user-activity-log.service.spec.ts
│ │ ├── entities
│ │ │ └── activity-log.entity.ts
│ │ ├── user-activity-log.controller.spec.ts
│ │ └── user-activity-log.controller.ts
│ ├── activity
│ │ ├── dto
│ │ │ ├── create-activity.dto.ts
│ │ │ └── filter-activity.dto.ts
│ │ ├── activity.module.ts
│ │ └── entities
│ │ │ └── activity.entity.ts
│ ├── nft-claim
│ │ ├── nft-claim.module.ts
│ │ ├── dto
│ │ │ └── claim-nft.dto.ts
│ │ └── nft-claim.controller.ts
│ ├── feedback
│ │ ├── index.ts
│ │ ├── feedback.module.ts
│ │ └── guards
│ │ │ └── admin.guard.ts
│ ├── time-trial
│ │ ├── time-trial.module.ts
│ │ └── time-trial.entity.ts
│ ├── migration
│ │ ├── index.ts
│ │ ├── dto
│ │ │ └── migration.dto.ts
│ │ └── guards
│ │ │ └── admin.guard.ts
│ ├── user-token-history
│ │ ├── index.ts
│ │ └── guards
│ │ │ └── admin.guard.ts
│ ├── puzzle-submission
│ │ ├── puzzle-submission.module.ts
│ │ ├── puzzle-submission.entity.ts
│ │ └── puzzle-submission.controller.ts
│ ├── streak
│ │ ├── dto
│ │ │ ├── record-activity.dto.ts
│ │ │ └── streak-stats.dto.ts
│ │ └── streak.module.ts
│ ├── user-settings
│ │ └── user-settings.module.ts
│ ├── token-verification
│ │ ├── decorators
│ │ │ └── token-payload.decorator.ts
│ │ ├── index.ts
│ │ ├── interfaces
│ │ │ └── token.interface.ts
│ │ └── interceptors
│ │ │ └── token-header.interceptor.ts
│ ├── milestone
│ │ ├── controllers
│ │ │ └── user-milestone.controller.ts
│ │ └── dto
│ │ │ └── milestone-achievement.dto.ts
│ ├── app.controller.ts
│ ├── app.controller.spec.ts
│ ├── puzzle-category
│ │ └── puzzle-category.module.ts
│ ├── multiplayer-queue
│ │ ├── multiplayer-queue.module.ts
│ │ └── dto
│ │ │ └── queue-stats.dto.ts
│ ├── puzzle-test-case
│ │ ├── index.ts
│ │ ├── puzzle-test-case.module.ts
│ │ └── guards
│ │ │ └── admin.guard.ts
│ └── puzzle-review
│ │ └── puzzle-review
│ │ ├── puzzle-review.module.ts
│ │ └── guards
│ │ └── admin.guard.ts
├── .prettierrc
├── tsconfig.build.json
├── test
│ ├── jest-e2e.json
│ └── app.e2e-spec.ts
├── nest-cli.json
├── config
│ ├── database.config.ts
│ └── app.config.ts
├── .eslintrc.js
├── tsconfig.json
└── .gitignore
├── onchain
├── .gitignore
├── src
│ ├── lib.cairo
│ └── utils.cairo
└── Scarb.toml
├── .tool-versions
├── .gitignore
├── frontend
├── .eslintrc.json
├── app
│ ├── favicon.ico
│ ├── fonts
│ │ ├── GeistVF.woff
│ │ └── GeistMonoVF.woff
│ ├── globals.css
│ ├── api
│ │ └── auth
│ │ │ └── [...nextauth]
│ │ │ └── route.ts
│ ├── puzzles
│ │ └── roadmap
│ │ │ └── app.tsx
│ ├── page.tsx
│ └── layout.js
├── public
│ ├── nftKey.jpeg
│ ├── nftCodex.jpeg
│ ├── nftCrown.jpeg
│ ├── nftCompass.jpeg
│ ├── nftTestimonial.JPG
│ └── vercel.svg
├── jsconfig.json
├── next.config.mjs
├── lib
│ ├── utils.js
│ ├── queryClient.js
│ └── authOptions.ts
├── postcss.config.mjs
├── components
│ ├── TechCardComponent.jsx
│ ├── SearchBar.jsx
│ ├── BlogCard.jsx
│ ├── BackToHome.jsx
│ ├── general
│ │ └── FeatureCard.jsx
│ ├── ui
│ │ ├── LoadingSpinner.jsx
│ │ ├── textarea.jsx
│ │ └── input.jsx
│ ├── puzzles
│ │ └── roadmap
│ │ │ ├── puzzleRoadmapData.ts
│ │ │ └── PuzzleTimeline.tsx
│ ├── AnimatedBlurBackground.jsx
│ ├── Rating.jsx
│ ├── AchievementCard.jsx
│ ├── Pagination.jsx
│ ├── TestComponent.jsx
│ └── GradientButton.jsx
├── components.json
├── .gitignore
├── store
│ └── game-progress
│ │ └── game-progress-store.js
└── tailwind.config.js
├── .DS_Store
├── package.json
└── .github
└── workflows
└── build.yml
/backend/src/rewards/rewards.controller.spec.ts:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/backend/src/session/enum/activityType.enum.ts:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/backend/src/puzzle-draft/dto/publish-draft.dto.ts:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/onchain/.gitignore:
--------------------------------------------------------------------------------
1 | target
2 | .snfoundry_cache/
3 |
--------------------------------------------------------------------------------
/.tool-versions:
--------------------------------------------------------------------------------
1 | scarb 2.8.4
2 | starknet-foundry 0.30.0
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .qodo
2 | .DS_Store
3 | node_modules
4 | dist
5 | .env
--------------------------------------------------------------------------------
/backend/src/api-key/entities/api-key.entity.ts:
--------------------------------------------------------------------------------
1 | export class ApiKey {}
2 |
--------------------------------------------------------------------------------
/backend/src/wallet/wallet.entity.ts:
--------------------------------------------------------------------------------
1 | // moved to entities/wallet.entity.ts
2 |
--------------------------------------------------------------------------------
/backend/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "trailingComma": "all"
4 | }
--------------------------------------------------------------------------------
/backend/src/analytics/entities/analytics.entity.ts:
--------------------------------------------------------------------------------
1 | export class Analytics {}
2 |
--------------------------------------------------------------------------------
/backend/src/api-key/dto/create-api-key.dto.ts:
--------------------------------------------------------------------------------
1 | export class CreateApiKeyDto {}
2 |
--------------------------------------------------------------------------------
/backend/src/analytics/dto/create-analytics.dto.ts:
--------------------------------------------------------------------------------
1 | export class CreateAnalyticsDto {}
2 |
--------------------------------------------------------------------------------
/backend/src/progress/dto/create-progress.dto.ts:
--------------------------------------------------------------------------------
1 | export class CreateProgressDto {}
2 |
--------------------------------------------------------------------------------
/backend/src/reward-shop/entities/reward-shop.entity.ts:
--------------------------------------------------------------------------------
1 | export class RewardShop {}
2 |
--------------------------------------------------------------------------------
/frontend/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["next/babel","next/core-web-vitals"]
3 | }
--------------------------------------------------------------------------------
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DistinctCodes/NFT-Scavenger-Hunt-Game/HEAD/.DS_Store
--------------------------------------------------------------------------------
/backend/src/reward-shop/dto/create-reward-shop.dto.ts:
--------------------------------------------------------------------------------
1 | export class CreateRewardShopDto {}
2 |
--------------------------------------------------------------------------------
/backend/src/achievements/dto/create-achievement.dto.ts:
--------------------------------------------------------------------------------
1 | export class CreateAchievementDto {}
2 |
--------------------------------------------------------------------------------
/backend/src/puzzle-comment/entities/puzzle-comment.entity.ts:
--------------------------------------------------------------------------------
1 | export class PuzzleComment {}
2 |
--------------------------------------------------------------------------------
/backend/src/user-inventory/entities/user-inventory.entity.ts:
--------------------------------------------------------------------------------
1 | export class UserInventory {}
2 |
--------------------------------------------------------------------------------
/backend/src/puzzle-comment/dto/create-puzzle-comment.dto.ts:
--------------------------------------------------------------------------------
1 | export class CreatePuzzleCommentDto {}
2 |
--------------------------------------------------------------------------------
/backend/src/user-inventory/dto/create-user-inventory.dto.ts:
--------------------------------------------------------------------------------
1 | export class CreateUserInventoryDto {}
2 |
--------------------------------------------------------------------------------
/backend/src/user-report-card/dto/create-user-report-card.dto.ts:
--------------------------------------------------------------------------------
1 | export class CreateUserReportCardDto {}
2 |
--------------------------------------------------------------------------------
/backend/src/common/enums/roles.enum.ts:
--------------------------------------------------------------------------------
1 | export enum Role {
2 | User = 'user',
3 | Admin = 'admin',
4 | }
5 |
--------------------------------------------------------------------------------
/backend/src/auth/enums/auth-type.enum.ts:
--------------------------------------------------------------------------------
1 | export enum AuthType {
2 | Bearer = 'Bearer',
3 | None = 'None',
4 | }
--------------------------------------------------------------------------------
/frontend/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DistinctCodes/NFT-Scavenger-Hunt-Game/HEAD/frontend/app/favicon.ico
--------------------------------------------------------------------------------
/backend/src/admin/admin-role.enum.ts:
--------------------------------------------------------------------------------
1 | export enum AdminRole {
2 | ADMIN = 'ADMIN',
3 | SUPERADMIN = 'SUPERADMIN',
4 | }
5 |
--------------------------------------------------------------------------------
/backend/src/badge/dto/assign-badge.dto.ts:
--------------------------------------------------------------------------------
1 | export class AssignBadgeDto {
2 | userId: number;
3 | badgeId: number;
4 | }
5 |
--------------------------------------------------------------------------------
/frontend/public/nftKey.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DistinctCodes/NFT-Scavenger-Hunt-Game/HEAD/frontend/public/nftKey.jpeg
--------------------------------------------------------------------------------
/backend/src/rate-limiter/rate-limit.interface.ts:
--------------------------------------------------------------------------------
1 | export interface RateLimitConfig {
2 | ttl: number;
3 | limit: number;
4 | }
5 |
--------------------------------------------------------------------------------
/frontend/public/nftCodex.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DistinctCodes/NFT-Scavenger-Hunt-Game/HEAD/frontend/public/nftCodex.jpeg
--------------------------------------------------------------------------------
/frontend/public/nftCrown.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DistinctCodes/NFT-Scavenger-Hunt-Game/HEAD/frontend/public/nftCrown.jpeg
--------------------------------------------------------------------------------
/frontend/app/fonts/GeistVF.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DistinctCodes/NFT-Scavenger-Hunt-Game/HEAD/frontend/app/fonts/GeistVF.woff
--------------------------------------------------------------------------------
/frontend/public/nftCompass.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DistinctCodes/NFT-Scavenger-Hunt-Game/HEAD/frontend/public/nftCompass.jpeg
--------------------------------------------------------------------------------
/backend/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
4 | }
5 |
--------------------------------------------------------------------------------
/frontend/app/fonts/GeistMonoVF.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DistinctCodes/NFT-Scavenger-Hunt-Game/HEAD/frontend/app/fonts/GeistMonoVF.woff
--------------------------------------------------------------------------------
/frontend/public/nftTestimonial.JPG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DistinctCodes/NFT-Scavenger-Hunt-Game/HEAD/frontend/public/nftTestimonial.JPG
--------------------------------------------------------------------------------
/frontend/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "jsx": "react",
4 | "paths": {
5 | "@/*": ["./*"]
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/backend/src/hint/create-hint.dto.ts:
--------------------------------------------------------------------------------
1 | export class CreateHintDto {
2 | puzzleId: string;
3 | content: string;
4 | unlockTimeInMinutes: number;
5 | }
6 |
--------------------------------------------------------------------------------
/frontend/next.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {
3 | // output: "export"
4 | };
5 |
6 | export default nextConfig;
7 |
--------------------------------------------------------------------------------
/backend/src/gameMechanics/dto/request-hint.dto.ts:
--------------------------------------------------------------------------------
1 | import { IsUUID } from "class-validator"
2 |
3 | export class RequestHintDto {
4 | @IsUUID()
5 | challengeId: string
6 | }
7 |
--------------------------------------------------------------------------------
/frontend/lib/utils.js:
--------------------------------------------------------------------------------
1 | import { clsx } from "clsx";
2 | import { twMerge } from "tailwind-merge"
3 |
4 | export function cn(...inputs) {
5 | return twMerge(clsx(inputs));
6 | }
7 |
--------------------------------------------------------------------------------
/backend/src/quiz/enums/question-type.enum.ts:
--------------------------------------------------------------------------------
1 | export enum QuestionType {
2 | MULTIPLE_CHOICE = 'multiple_choice',
3 | TRUE_FALSE = 'true_false',
4 | SINGLE_CHOICE = 'single_choice'
5 | }
--------------------------------------------------------------------------------
/frontend/postcss.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('postcss-load-config').Config} */
2 | const config = {
3 | plugins: {
4 | tailwindcss: {},
5 | },
6 | };
7 |
8 | export default config;
9 |
--------------------------------------------------------------------------------
/backend/src/app.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@nestjs/common';
2 |
3 | @Injectable()
4 | export class AppService {
5 | getHello(): string {
6 | return 'Hello World!';
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/backend/src/geostats/dto/create-geostat.dto.ts:
--------------------------------------------------------------------------------
1 | import { IsIP, IsNotEmpty } from 'class-validator';
2 |
3 | export class CreateGeoStatDto {
4 | @IsNotEmpty()
5 | @IsIP()
6 | ipAddress: string;
7 | }
--------------------------------------------------------------------------------
/onchain/src/lib.cairo:
--------------------------------------------------------------------------------
1 | pub mod interface;
2 | pub mod utils;
3 |
4 | pub mod contracts {
5 | pub mod mock_1155_receiver;
6 | pub mod scavenger_hunt;
7 | pub mod scavenger_hunt_nft;
8 | }
9 |
--------------------------------------------------------------------------------
/backend/src/daily-reward/dto/daily-checkin.dto.ts:
--------------------------------------------------------------------------------
1 | import { IsNotEmpty, IsString } from 'class-validator';
2 |
3 | export class DailyCheckinDto {
4 | @IsString()
5 | @IsNotEmpty()
6 | userId: string;
7 | }
--------------------------------------------------------------------------------
/backend/src/common/decorators/roles.decorator.ts:
--------------------------------------------------------------------------------
1 | import { SetMetadata } from '@nestjs/common';
2 |
3 | export const ROLES_KEY = 'roles';
4 | export const Roles = (...roles: string[]) => SetMetadata(ROLES_KEY, roles);
5 |
--------------------------------------------------------------------------------
/backend/src/nft-marketplace-stub/entities/nft-item.entity.ts:
--------------------------------------------------------------------------------
1 | export class NftItem {
2 | id: string;
3 | name: string;
4 | imageUrl: string;
5 | price: number;
6 | description: string;
7 | owner: string;
8 | }
--------------------------------------------------------------------------------
/backend/src/quiz/dto/update-quiz.dto.ts:
--------------------------------------------------------------------------------
1 | import { PartialType } from '@nestjs/swagger';
2 | import { CreateQuizDto } from './create-quiz.dto';
3 |
4 | export class UpdateQuizDto extends PartialType(CreateQuizDto) {}
5 |
--------------------------------------------------------------------------------
/backend/src/admin/guards/jwt-auth.guard.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@nestjs/common';
2 | import { AuthGuard } from '@nestjs/passport';
3 |
4 | @Injectable()
5 | export class JwtAuthGuard extends AuthGuard('admin-jwt') {}
6 |
--------------------------------------------------------------------------------
/backend/src/maintenance-mode/decorators/admin-only.decorator.ts:
--------------------------------------------------------------------------------
1 | import { SetMetadata } from "@nestjs/common"
2 |
3 | export const ADMIN_ONLY_KEY = "adminOnly"
4 | export const AdminOnly = () => SetMetadata(ADMIN_ONLY_KEY, true)
5 |
--------------------------------------------------------------------------------
/backend/src/puzzle/dto/update-puzzle.dto.ts:
--------------------------------------------------------------------------------
1 | import { PartialType } from '@nestjs/swagger';
2 | import { CreatePuzzleDto } from './create-puzzle.dto';
3 |
4 | export class UpdatePuzzleDto extends PartialType(CreatePuzzleDto) {}
5 |
--------------------------------------------------------------------------------
/backend/src/reports/dto/update-report.dto.ts:
--------------------------------------------------------------------------------
1 | import { PartialType } from '@nestjs/swagger';
2 | import { CreateReportDto } from './create-report.dto';
3 |
4 | export class UpdateReportDto extends PartialType(CreateReportDto) {}
5 |
--------------------------------------------------------------------------------
/backend/src/api-key/dto/update-api-key.dto.ts:
--------------------------------------------------------------------------------
1 | import { PartialType } from '@nestjs/swagger';
2 | import { CreateApiKeyDto } from './create-api-key.dto';
3 |
4 | export class UpdateApiKeyDto extends PartialType(CreateApiKeyDto) {}
5 |
--------------------------------------------------------------------------------
/backend/src/content/dto/update-content.dto.ts:
--------------------------------------------------------------------------------
1 | import { PartialType } from '@nestjs/mapped-types';
2 | import { CreateContentDto } from './create-content.dto';
3 |
4 | export class UpdateContentDto extends PartialType(CreateContentDto) {}
--------------------------------------------------------------------------------
/backend/src/promo-code/dto/redeem-promo-code.dto.ts:
--------------------------------------------------------------------------------
1 | // src/promo-code/dto/redeem-promo-code.dto.ts
2 | import { IsString } from 'class-validator';
3 |
4 | export class RedeemPromoCodeDto {
5 | @IsString()
6 | code: string;
7 | }
8 |
--------------------------------------------------------------------------------
/backend/src/puzzle-draft/dto/update-draft.dto.ts:
--------------------------------------------------------------------------------
1 | import { PartialType } from '@nestjs/mapped-types';
2 | import { CreateDraftDto } from './create-draft.dto';
3 |
4 | export class UpdateDraftDto extends PartialType(CreateDraftDto) {}
5 |
--------------------------------------------------------------------------------
/backend/src/admin/dto/login-admin.dto.ts:
--------------------------------------------------------------------------------
1 | import { IsEmail, IsNotEmpty } from 'class-validator';
2 |
3 | export class LoginAdminDto {
4 | @IsEmail()
5 | email: string;
6 |
7 | @IsNotEmpty()
8 | password: string;
9 | }
10 |
--------------------------------------------------------------------------------
/backend/src/progress/dto/update-progress.dto.ts:
--------------------------------------------------------------------------------
1 | import { PartialType } from '@nestjs/mapped-types';
2 | import { CreateProgressDto } from './create-progress.dto';
3 |
4 | export class UpdateProgressDto extends PartialType(CreateProgressDto) {}
5 |
--------------------------------------------------------------------------------
/backend/src/analytics/dto/update-analytics.dto.ts:
--------------------------------------------------------------------------------
1 | import { PartialType } from '@nestjs/swagger';
2 | import { CreateAnalyticsDto } from './create-analytics.dto';
3 |
4 | export class UpdateAnalyticsDto extends PartialType(CreateAnalyticsDto) {}
5 |
--------------------------------------------------------------------------------
/backend/src/gameMechanics/dto/update-challenge.dto.ts:
--------------------------------------------------------------------------------
1 | import { PartialType } from "@nestjs/mapped-types"
2 | import { CreateChallengeDto } from "./create-challenge.dto"
3 |
4 | export class UpdateChallengeDto extends PartialType(CreateChallengeDto) {}
5 |
--------------------------------------------------------------------------------
/backend/src/reward-shop/dto/update-reward-shop.dto.ts:
--------------------------------------------------------------------------------
1 | import { PartialType } from '@nestjs/swagger';
2 | import { CreateRewardShopDto } from './create-reward-shop.dto';
3 |
4 | export class UpdateRewardShopDto extends PartialType(CreateRewardShopDto) {}
5 |
--------------------------------------------------------------------------------
/backend/test/jest-e2e.json:
--------------------------------------------------------------------------------
1 | {
2 | "moduleFileExtensions": ["js", "json", "ts"],
3 | "rootDir": ".",
4 | "testEnvironment": "node",
5 | "testRegex": ".e2e-spec.ts$",
6 | "transform": {
7 | "^.+\\.(t|j)s$": "ts-jest"
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/backend/src/achievements/dto/update-achievement.dto.ts:
--------------------------------------------------------------------------------
1 | import { PartialType } from '@nestjs/swagger';
2 | import { CreateAchievementDto } from './create-achievement.dto';
3 |
4 | export class UpdateAchievementDto extends PartialType(CreateAchievementDto) {}
5 |
--------------------------------------------------------------------------------
/backend/src/admin/roles.decorator.ts:
--------------------------------------------------------------------------------
1 | import { SetMetadata } from '@nestjs/common';
2 | import { AdminRole } from './admin-role.enum';
3 |
4 | export const ROLES_KEY = 'roles';
5 | export const Roles = (...roles: AdminRole[]) => SetMetadata(ROLES_KEY, roles);
6 |
--------------------------------------------------------------------------------
/backend/src/referral/dto/referral-stats.dto.ts:
--------------------------------------------------------------------------------
1 | export class ReferralStatsDto {
2 | totalInvites: number
3 | successfulInvites: number
4 | conversionRate: number
5 | totalBonusEarned: number
6 | pendingBonuses: number
7 | processedBonuses: number
8 | }
9 |
--------------------------------------------------------------------------------
/backend/src/user-ranking/dto/update-user-ranking.dto.ts:
--------------------------------------------------------------------------------
1 | import { PartialType } from '@nestjs/swagger';
2 | import { CreateUserRankingDto } from './create-user-ranking.dto';
3 |
4 | export class UpdateUserRankingDto extends PartialType(CreateUserRankingDto) {}
5 |
--------------------------------------------------------------------------------
/backend/src/maintenance-mode/decorators/maintenance-exempt.decorator.ts:
--------------------------------------------------------------------------------
1 | import { SetMetadata } from "@nestjs/common"
2 |
3 | export const MAINTENANCE_EXEMPT_KEY = "maintenanceExempt"
4 | export const MaintenanceExempt = () => SetMetadata(MAINTENANCE_EXEMPT_KEY, true)
5 |
--------------------------------------------------------------------------------
/backend/src/puzzle-translation/dto/update-translation.dto.ts:
--------------------------------------------------------------------------------
1 | import { PartialType } from '@nestjs/mapped-types';
2 | import { CreateTranslationDto } from './create-translation.dto';
3 |
4 | export class UpdateTranslationDto extends PartialType(CreateTranslationDto) {}
--------------------------------------------------------------------------------
/backend/nest-cli.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/nest-cli",
3 | "collection": "@nestjs/schematics",
4 | "sourceRoot": "src",
5 | "compilerOptions": {
6 | "deleteOutDir": true,
7 | "plugins": ["@nestjs/swagger"]
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/backend/src/admin/dto/create-admin.dto.ts:
--------------------------------------------------------------------------------
1 | import { IsEmail, IsNotEmpty, MinLength } from 'class-validator';
2 |
3 | export class CreateAdminDto {
4 | @IsEmail()
5 | email: string;
6 |
7 | @IsNotEmpty()
8 | @MinLength(6)
9 | password: string;
10 | }
11 |
--------------------------------------------------------------------------------
/backend/src/puzzle-comment/dto/update-puzzle-comment.dto.ts:
--------------------------------------------------------------------------------
1 | import { PartialType } from '@nestjs/swagger';
2 | import { CreatePuzzleCommentDto } from './create-puzzle-comment.dto';
3 |
4 | export class UpdatePuzzleCommentDto extends PartialType(CreatePuzzleCommentDto) {}
5 |
--------------------------------------------------------------------------------
/backend/src/rate-limiter/rate-limit.decorator.ts:
--------------------------------------------------------------------------------
1 | import { SetMetadata } from '@nestjs/common';
2 | import { RATE_LIMIT_KEY } from './rate-limit.guard';
3 |
4 | export const RateLimit = (config: { ttl: number; limit: number }) =>
5 | SetMetadata(RATE_LIMIT_KEY, config);
6 |
--------------------------------------------------------------------------------
/backend/src/user-inventory/dto/update-user-inventory.dto.ts:
--------------------------------------------------------------------------------
1 | import { PartialType } from '@nestjs/swagger';
2 | import { CreateUserInventoryDto } from './create-user-inventory.dto';
3 |
4 | export class UpdateUserInventoryDto extends PartialType(CreateUserInventoryDto) {}
5 |
--------------------------------------------------------------------------------
/backend/src/auth/decorators/auth-decorator.ts:
--------------------------------------------------------------------------------
1 | import { SetMetadata } from '@nestjs/common';
2 | import { AuthType } from '../enums/auth-type.enum';
3 |
4 | export const AUTH_TYPE_KEY = 'authType';
5 | export const Auth = (authType: AuthType) => SetMetadata(AUTH_TYPE_KEY, authType);
--------------------------------------------------------------------------------
/backend/src/reports/dto/create-report.dto.ts:
--------------------------------------------------------------------------------
1 | import { IsNotEmpty, IsNumber, IsString } from 'class-validator';
2 |
3 | export class CreateReportDto {
4 | @IsNumber()
5 | puzzleId: number;
6 |
7 | @IsString()
8 | @IsNotEmpty()
9 | message: string;
10 | }
11 |
--------------------------------------------------------------------------------
/backend/src/puzzle-access-log/dto/log-access.dto.ts:
--------------------------------------------------------------------------------
1 | import { IsString, IsNotEmpty } from 'class-validator';
2 |
3 | export class LogAccessDto {
4 | @IsString()
5 | @IsNotEmpty()
6 | userId: string;
7 |
8 | @IsString()
9 | @IsNotEmpty()
10 | puzzleId: string;
11 | }
--------------------------------------------------------------------------------
/backend/src/puzzle-dependency/dto/update-puzzle-dependency.dto.ts:
--------------------------------------------------------------------------------
1 | import { PartialType } from '@nestjs/swagger';
2 | import { CreatePuzzleDependencyDto } from './create-puzzle-dependency.dto';
3 |
4 | export class UpdatePuzzleDependencyDto extends PartialType(CreatePuzzleDependencyDto) {}
--------------------------------------------------------------------------------
/backend/src/user-report-card/dto/update-user-report-card.dto.ts:
--------------------------------------------------------------------------------
1 | import { PartialType } from '@nestjs/mapped-types';
2 | import { CreateUserReportCardDto } from './create-user-report-card.dto';
3 |
4 | export class UpdateUserReportCardDto extends PartialType(CreateUserReportCardDto) {}
5 |
--------------------------------------------------------------------------------
/backend/src/user-reaction/dto/update-reaction.dto.ts:
--------------------------------------------------------------------------------
1 | import { PartialType, OmitType } from "@nestjs/mapped-types"
2 | import { CreateReactionDto } from "./create-reaction.dto"
3 |
4 | export class UpdateReactionDto extends PartialType(OmitType(CreateReactionDto, ["userId", "contentId"] as const)) {}
5 |
--------------------------------------------------------------------------------
/backend/src/user/dto/link-wallet.dto.ts:
--------------------------------------------------------------------------------
1 | import { IsString, Matches } from 'class-validator';
2 |
3 | export class LinkWalletDto {
4 | @IsString()
5 | @Matches(/^0x[a-fA-F0-9]{40}$/, {
6 | message: 'Wallet address must be a valid Ethereum address',
7 | })
8 | walletAddress: string;
9 | }
10 |
--------------------------------------------------------------------------------
/backend/src/referral/dto/create-referral-code.dto.ts:
--------------------------------------------------------------------------------
1 | import { IsOptional, IsDateString, IsBoolean } from "class-validator"
2 |
3 | export class CreateReferralCodeDto {
4 | @IsOptional()
5 | @IsDateString()
6 | expiresAt?: string
7 |
8 | @IsOptional()
9 | @IsBoolean()
10 | isActive?: boolean
11 | }
12 |
--------------------------------------------------------------------------------
/backend/src/content-rating/dto/create-rating.dto.ts:
--------------------------------------------------------------------------------
1 | import { IsInt, IsString, Max, Min } from 'class-validator';
2 |
3 | export class CreateRatingDto {
4 | @IsString()
5 | userId: string;
6 |
7 | @IsString()
8 | contentId: string;
9 |
10 | @IsInt()
11 | @Min(1)
12 | @Max(5)
13 | rating: number;
14 | }
15 |
--------------------------------------------------------------------------------
/backend/src/gameMechanics/dto/submit-puzzle.dto.ts:
--------------------------------------------------------------------------------
1 | import { IsString, IsUUID, IsOptional, IsInt } from "class-validator"
2 |
3 | export class SubmitPuzzleDto {
4 | @IsUUID()
5 | challengeId: string
6 |
7 | @IsString()
8 | answer: string
9 |
10 | @IsInt()
11 | @IsOptional()
12 | timeTaken?: number
13 | }
14 |
--------------------------------------------------------------------------------
/backend/src/user/dto/update-user-profile.dto.ts:
--------------------------------------------------------------------------------
1 | import { IsOptional, IsString, MaxLength, IsUrl } from 'class-validator';
2 |
3 | export class UpdateUserProfileDto {
4 | @IsOptional()
5 | @IsString()
6 | @MaxLength(160)
7 | bio?: string;
8 |
9 | @IsOptional()
10 | @IsUrl()
11 | avatarUrl?: string;
12 | }
13 |
--------------------------------------------------------------------------------
/backend/src/reports/reports.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { ReportsService } from './reports.service';
3 | import { ReportsController } from './reports.controller';
4 |
5 | @Module({
6 | controllers: [ReportsController],
7 | providers: [ReportsService],
8 | })
9 | export class ReportsModule {}
10 |
--------------------------------------------------------------------------------
/frontend/app/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | body {
6 | font-family: Arial, Helvetica, sans-serif;
7 | }
8 |
9 | @layer utilities {
10 | .text-balance {
11 | text-wrap: balance;
12 | }
13 | }
14 |
15 | @layer base {
16 | :root {
17 | --radius: 0.5rem;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/backend/src/hint/hint.entity.ts:
--------------------------------------------------------------------------------
1 | import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
2 |
3 | @Entity()
4 | export class Hint {
5 | @PrimaryGeneratedColumn()
6 | id: number;
7 |
8 | @Column()
9 | puzzleId: string;
10 |
11 | @Column()
12 | content: string;
13 |
14 | @Column()
15 | unlockTimeInMinutes: number;
16 | }
17 |
--------------------------------------------------------------------------------
/backend/src/api-key/api-key.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { ApiKeyService } from './api-key.service';
3 | import { ApiKeyController } from './api-key.controller';
4 |
5 | @Module({
6 | providers: [ApiKeyService],
7 | controllers: [ApiKeyController],
8 | exports: [ApiKeyService],
9 | })
10 | export class ApiKeyModule {}
11 |
--------------------------------------------------------------------------------
/frontend/components/TechCardComponent.jsx:
--------------------------------------------------------------------------------
1 | const TechCard = ({ title, description }) => (
2 |
3 |
{title}
4 |
{description}
5 |
6 | );
7 |
8 | export default TechCard;
9 |
--------------------------------------------------------------------------------
/backend/src/in-app-notifications/dto/mark-read.dto.ts:
--------------------------------------------------------------------------------
1 | import { IsArray, IsNumber } from 'class-validator';
2 | import { ApiProperty } from '@nestjs/swagger';
3 |
4 | export class MarkReadDto {
5 | @ApiProperty({ description: 'Array of notification IDs to mark as read', type: [Number] })
6 | @IsArray()
7 | @IsNumber({}, { each: true })
8 | notificationIds: number[];
9 | }
--------------------------------------------------------------------------------
/backend/src/puzzle-draft/dto/create-draft.dto.ts:
--------------------------------------------------------------------------------
1 | import { IsNotEmpty, IsString, IsOptional, IsObject } from 'class-validator';
2 |
3 | export class CreateDraftDto {
4 | @IsString()
5 | @IsNotEmpty()
6 | title: string;
7 |
8 | @IsOptional()
9 | @IsString()
10 | description?: string;
11 |
12 | @IsObject()
13 | content: Record;
14 | }
15 |
--------------------------------------------------------------------------------
/backend/src/puzzle-dependency/interfaces/eligibility-result.interface.ts:
--------------------------------------------------------------------------------
1 | export interface EligibilityResult {
2 | isEligible: boolean;
3 | reason?: string;
4 | missingDependencies?: string[];
5 | completedDependencies?: string[];
6 | }
7 |
8 | export interface DependencyChain {
9 | puzzleId: string;
10 | dependencies: string[];
11 | level: number;
12 | }
13 |
--------------------------------------------------------------------------------
/backend/src/puzzle-translation/dto/create-translation.dto.ts:
--------------------------------------------------------------------------------
1 | import { IsNotEmpty, IsString } from 'class-validator';
2 |
3 | export class CreateTranslationDto {
4 | @IsNotEmpty()
5 | @IsString()
6 | puzzleId: string;
7 |
8 | @IsString()
9 | language: string;
10 |
11 | @IsString()
12 | title: string;
13 |
14 | @IsString()
15 | description: string;
16 | }
--------------------------------------------------------------------------------
/backend/src/referral/dto/create-invite.dto.ts:
--------------------------------------------------------------------------------
1 | import { IsEmail, IsString, IsOptional, IsDateString } from "class-validator"
2 |
3 | export class CreateInviteDto {
4 | @IsEmail()
5 | email: string
6 |
7 | @IsString()
8 | referralCode: string
9 |
10 | @IsOptional()
11 | @IsDateString()
12 | expiresAt?: string
13 |
14 | @IsOptional()
15 | metadata?: any
16 | }
17 |
--------------------------------------------------------------------------------
/backend/src/analytics/analytics.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { AnalyticsService } from './analytics.service';
3 | import { AnalyticsController } from './analytics.controller';
4 |
5 | @Module({
6 | providers: [AnalyticsService],
7 | controllers: [AnalyticsController],
8 | exports: [AnalyticsService],
9 | })
10 | export class AnalyticsModule {}
11 |
--------------------------------------------------------------------------------
/backend/src/puzzle-fork/dto/create-fork.dto.ts:
--------------------------------------------------------------------------------
1 |
2 | import { IsString, IsNotEmpty, IsInt, IsOptional } from 'class-validator';
3 |
4 | export class CreateForkDto {
5 | @IsString()
6 | @IsNotEmpty()
7 | originalPuzzleId: string;
8 |
9 |
10 | @IsInt()
11 | @IsOptional()
12 | version?: number;
13 |
14 |
15 | @IsString()
16 | @IsOptional()
17 | newTitle?: string;
18 | }
--------------------------------------------------------------------------------
/backend/src/user/dto/create-user.dto.ts:
--------------------------------------------------------------------------------
1 | import { IsEmail, IsString, Length, Matches } from 'class-validator';
2 |
3 | export class CreateUserDto {
4 | @IsString()
5 | @Length(3, 30)
6 | @Matches(/^[a-zA-Z0-9_]+$/, {
7 | message: 'Username can only contain letters, numbers, and underscores',
8 | })
9 | username: string;
10 |
11 | @IsEmail()
12 | email: string;
13 | }
14 |
--------------------------------------------------------------------------------
/backend/src/user-ranking/dto/create-user-ranking.dto.ts:
--------------------------------------------------------------------------------
1 | import { ApiProperty } from '@nestjs/swagger';
2 |
3 | export class UserRankDto {
4 | @ApiProperty()
5 | userId: string;
6 |
7 | @ApiProperty()
8 | score: number;
9 |
10 | @ApiProperty()
11 | achievements: number;
12 |
13 | @ApiProperty()
14 | activityPoints: number;
15 |
16 | @ApiProperty()
17 | rank: number;
18 | }
--------------------------------------------------------------------------------
/frontend/components/SearchBar.jsx:
--------------------------------------------------------------------------------
1 | export default function SearchBar({ value, onChange }) {
2 | return (
3 |
10 | );
11 | }
12 |
--------------------------------------------------------------------------------
/backend/src/geostats/entities/geostat.entity.ts:
--------------------------------------------------------------------------------
1 | import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn } from 'typeorm';
2 |
3 | @Entity('geostats')
4 | export class GeoStats {
5 | @PrimaryGeneratedColumn('uuid')
6 | id: string;
7 |
8 | @Column()
9 | ipAddress: string;
10 |
11 | @Column()
12 | country: string;
13 |
14 | @CreateDateColumn()
15 | timestamp: Date;
16 | }
--------------------------------------------------------------------------------
/backend/src/reward-shop/reward-shop.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { RewardShopService } from './reward-shop.service';
3 | import { RewardShopController } from './reward-shop.controller';
4 |
5 | @Module({
6 | providers: [RewardShopService],
7 | controllers: [RewardShopController],
8 | exports: [RewardShopService],
9 | })
10 | export class RewardShopModule {}
11 |
--------------------------------------------------------------------------------
/backend/src/puzzle-versioning/dto/create-puzzle-version.dto.ts:
--------------------------------------------------------------------------------
1 |
2 | import { IsString, IsNotEmpty, IsObject, IsOptional } from 'class-validator';
3 |
4 | export class CreatePuzzleVersionDto {
5 | @IsString()
6 | @IsOptional()
7 | puzzleId?: string;
8 |
9 | @IsString()
10 | @IsNotEmpty()
11 | title: string;
12 |
13 | @IsObject()
14 | @IsNotEmpty()
15 | content: Record;
16 | }
--------------------------------------------------------------------------------
/backend/src/progress/dto/progress-response.dto.ts:
--------------------------------------------------------------------------------
1 | import { ApiProperty } from '@nestjs/swagger';
2 |
3 | export class ProgressResponseDto {
4 | @ApiProperty()
5 | userId: string;
6 |
7 | @ApiProperty()
8 | completedPuzzles: number;
9 |
10 | @ApiProperty()
11 | totalPuzzles: number;
12 |
13 | @ApiProperty()
14 | percentComplete: number;
15 |
16 | @ApiProperty()
17 | lastUpdated: Date;
18 | }
19 |
--------------------------------------------------------------------------------
/frontend/lib/queryClient.js:
--------------------------------------------------------------------------------
1 | "use client";
2 | import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
3 | import { useState } from "react";
4 |
5 | export default function Providers({ children }) {
6 | const [queryClient] = useState(() => new QueryClient());
7 |
8 | return (
9 |
10 | {children}
11 |
12 | );
13 | }
--------------------------------------------------------------------------------
/backend/src/nft-marketplace-stub/nft-marketplace-stub.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { NftMarketplaceStubService } from './nft-marketplace-stub.service';
3 | import { NftMarketplaceStubController } from './nft-marketplace-stub.controller';
4 |
5 | @Module({
6 | controllers: [NftMarketplaceStubController],
7 | providers: [NftMarketplaceStubService],
8 | })
9 | export class NftMarketplaceStubModule {}
--------------------------------------------------------------------------------
/backend/src/puzzle-comment/puzzle-comment.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { PuzzleCommentService } from './puzzle-comment.service';
3 | import { PuzzleCommentController } from './puzzle-comment.controller';
4 |
5 | @Module({
6 | providers: [PuzzleCommentService],
7 | controllers: [PuzzleCommentController],
8 | exports: [PuzzleCommentService],
9 | })
10 | export class PuzzleCommentModule {}
11 |
--------------------------------------------------------------------------------
/backend/src/wallet/entities/wallet.entity.ts:
--------------------------------------------------------------------------------
1 | import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from 'typeorm';
2 |
3 | @Entity()
4 | export class Wallet {
5 | @PrimaryGeneratedColumn('uuid')
6 | id: string;
7 |
8 | @Column({ unique: true })
9 | address: string;
10 |
11 | @CreateDateColumn()
12 | createdAt: Date;
13 |
14 | @UpdateDateColumn()
15 | updatedAt: Date;
16 | }
17 |
--------------------------------------------------------------------------------
/frontend/app/api/auth/[...nextauth]/route.ts:
--------------------------------------------------------------------------------
1 | import NextAuth from "next-auth";
2 | import GitHubProvider from "next-auth/providers/github";
3 | import TwitterProvider from "next-auth/providers/twitter";
4 | import DiscordProvider from "next-auth/providers/discord";
5 | import { authOptions } from "@/lib/authOptions"; // move this out for reuse
6 |
7 | const handler = NextAuth(authOptions);
8 | export { handler as GET, handler as POST };
9 |
--------------------------------------------------------------------------------
/backend/src/user-report-card/user-report-card.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { UserReportCardController } from './user-report-card.controller';
3 | import { UserReportCardService } from './user-report-card.service';
4 |
5 | @Module({
6 | controllers: [UserReportCardController],
7 | providers: [UserReportCardService],
8 | exports: [UserReportCardService],
9 | })
10 | export class UserReportCardModule {}
--------------------------------------------------------------------------------
/backend/src/audit-log/Dto/filter-audit-log.dto.ts:
--------------------------------------------------------------------------------
1 | import { IsOptional, IsString, IsDateString } from 'class-validator';
2 |
3 | export class FilterAuditLogDto {
4 | @IsOptional()
5 | @IsString()
6 | userId?: string;
7 |
8 | @IsOptional()
9 | @IsString()
10 | action?: string;
11 |
12 | @IsOptional()
13 | @IsDateString()
14 | startDate?: string;
15 |
16 | @IsOptional()
17 | @IsDateString()
18 | endDate?: string;
19 | }
20 |
--------------------------------------------------------------------------------
/backend/src/auth/decorators/current-user.decorator.ts:
--------------------------------------------------------------------------------
1 | import { createParamDecorator, type ExecutionContext } from "@nestjs/common"
2 | import type { User } from "../entities/user.entity"
3 |
4 | export const CurrentUser = createParamDecorator((data: keyof User | undefined, ctx: ExecutionContext): User | any => {
5 | const request = ctx.switchToHttp().getRequest()
6 | const user = request.user
7 |
8 | return data ? user?.[data] : user
9 | })
10 |
--------------------------------------------------------------------------------
/backend/src/hint/hint.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { TypeOrmModule } from '@nestjs/typeorm';
3 | import { Hint } from './hint.entity';
4 | import { HintService } from './hint.service';
5 | import { HintController } from './hint.controller';
6 |
7 | @Module({
8 | imports: [TypeOrmModule.forFeature([Hint])],
9 | providers: [HintService],
10 | controllers: [HintController],
11 | })
12 | export class HintModule {}
13 |
--------------------------------------------------------------------------------
/backend/src/daily-reward/entities/daily-reward-log.entity.ts:
--------------------------------------------------------------------------------
1 | import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, Index } from 'typeorm';
2 |
3 | @Entity('daily_reward_logs')
4 | export class DailyRewardLog {
5 | @PrimaryGeneratedColumn('uuid')
6 | id: string;
7 |
8 | @Index()
9 | @Column()
10 | userId: string;
11 |
12 | @Column({ default: 1 })
13 | streak: number;
14 |
15 | @CreateDateColumn()
16 | timestamp: Date;
17 | }
--------------------------------------------------------------------------------
/backend/src/user-activity-log/dto/filter-activity.dto.ts:
--------------------------------------------------------------------------------
1 | import { IsOptional, IsString, IsDateString } from 'class-validator';
2 |
3 | export class FilterActivityDto {
4 | @IsOptional()
5 | @IsString()
6 | userId?: string;
7 |
8 | @IsOptional()
9 | @IsString()
10 | actionType?: string;
11 |
12 | @IsOptional()
13 | @IsDateString()
14 | startDate?: string;
15 |
16 | @IsOptional()
17 | @IsDateString()
18 | endDate?: string;
19 | }
20 |
--------------------------------------------------------------------------------
/backend/src/activity/dto/create-activity.dto.ts:
--------------------------------------------------------------------------------
1 | import { ApiProperty } from '@nestjs/swagger';
2 | import { ActivityType } from '../entities/activity.entity';
3 | import { IsEnum, IsOptional, IsObject } from 'class-validator';
4 |
5 | export class CreateActivityDto {
6 | @ApiProperty({ enum: ActivityType })
7 | @IsEnum(ActivityType)
8 | type: ActivityType;
9 |
10 | @ApiProperty()
11 | @IsOptional()
12 | @IsObject()
13 | metadata?: Record;
14 | }
--------------------------------------------------------------------------------
/backend/src/puzzle-dependency/dto/mark-completion.dto.ts:
--------------------------------------------------------------------------------
1 | import { IsString, IsNotEmpty } from 'class-validator';
2 | import { ApiProperty } from '@nestjs/swagger';
3 |
4 | export class MarkCompletionDto {
5 | @ApiProperty({ description: 'User ID who completed the puzzle' })
6 | @IsString()
7 | @IsNotEmpty()
8 | userId: string;
9 |
10 | @ApiProperty({ description: 'Puzzle ID that was completed' })
11 | @IsString()
12 | @IsNotEmpty()
13 | puzzleId: string;
14 | }
--------------------------------------------------------------------------------
/backend/src/nft-claim/nft-claim.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { NFTClaimController } from './nft-claim.controller';
3 | import { NFTClaimService } from './nft-claim.service';
4 | import { StarkNetHandlerService } from './providers/starknet-handler.service';
5 |
6 | @Module({
7 | controllers: [NFTClaimController],
8 | providers: [NFTClaimService, StarkNetHandlerService],
9 | exports: [NFTClaimService],
10 | })
11 | export class NFTClaimModule {}
--------------------------------------------------------------------------------
/frontend/components/BlogCard.jsx:
--------------------------------------------------------------------------------
1 | export default function BlogCard({ post }) {
2 | return (
3 |
4 |
{post.title}
5 |
6 | {new Date(post.date).toDateString()}
7 |
8 |
{post.summary}
9 |
10 | );
11 | }
12 |
--------------------------------------------------------------------------------
/backend/src/puzzle-dependency/dto/check-eligibility.dto.ts:
--------------------------------------------------------------------------------
1 | import { IsString, IsNotEmpty } from 'class-validator';
2 | import { ApiProperty } from '@nestjs/swagger';
3 |
4 | export class CheckEligibilityDto {
5 | @ApiProperty({ description: 'User ID to check eligibility for' })
6 | @IsString()
7 | @IsNotEmpty()
8 | userId: string;
9 |
10 | @ApiProperty({ description: 'Puzzle ID to check eligibility for' })
11 | @IsString()
12 | @IsNotEmpty()
13 | puzzleId: string;
14 | }
--------------------------------------------------------------------------------
/backend/src/wallet/wallet.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { TypeOrmModule } from '@nestjs/typeorm';
3 | import { WalletService } from './wallet.service';
4 | import { WalletController } from './wallet.controller';
5 | import { Wallet } from './entities/wallet.entity';
6 |
7 | @Module({
8 | imports: [TypeOrmModule.forFeature([Wallet])],
9 | providers: [WalletService],
10 | controllers: [WalletController],
11 | })
12 | export class WalletModule {}
13 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "@nestjs/common": "^11.1.3",
4 | "@nestjs/core": "^11.1.3",
5 | "@nestjs/testing": "^11.1.3",
6 | "@nestjs/typeorm": "^11.0.0",
7 | "dotenv": "^16.4.7",
8 | "nodemailer": "^6.10.0",
9 | "pg": "^8.16.3",
10 | "reflect-metadata": "^0.2.2",
11 | "typeorm": "^0.3.25"
12 | },
13 | "devDependencies": {
14 | "@types/jest": "^30.0.0",
15 | "jest": "^30.0.3",
16 | "ts-jest": "^29.4.0"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/backend/src/audit-log/entities/audit-log.entity.ts:
--------------------------------------------------------------------------------
1 | import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn } from 'typeorm';
2 |
3 | @Entity('audit_logs')
4 | export class AuditLog {
5 | @PrimaryGeneratedColumn('uuid')
6 | id: string;
7 |
8 | @Column()
9 | userId: string;
10 |
11 | @Column()
12 | action: string;
13 |
14 | @CreateDateColumn()
15 | timestamp: Date;
16 |
17 | @Column({ type: 'json', nullable: true })
18 | meta: Record;
19 | }
20 |
--------------------------------------------------------------------------------
/backend/src/user/user.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { TypeOrmModule } from '@nestjs/typeorm';
3 | import { User } from './entities/user.entity';
4 | import { UserService } from './user.service';
5 | import { UserController } from './user.controller';
6 |
7 | @Module({
8 | imports: [TypeOrmModule.forFeature([User])],
9 | providers: [UserService],
10 | controllers: [UserController],
11 | exports: [UserService],
12 | })
13 | export class UserModule {}
14 |
--------------------------------------------------------------------------------
/backend/src/progress/progress.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { TypeOrmModule } from '@nestjs/typeorm';
3 | import { Progress } from './progress.entity';
4 | import { ProgressService } from './progress.service';
5 | import { ProgressController } from './progress.controller';
6 |
7 | @Module({
8 | imports: [TypeOrmModule.forFeature([Progress])],
9 | providers: [ProgressService],
10 | controllers: [ProgressController],
11 | })
12 | export class ProgressModule {}
13 |
--------------------------------------------------------------------------------
/backend/src/puzzle/puzzle.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { TypeOrmModule } from '@nestjs/typeorm';
3 | import { Puzzle } from './puzzle.entity';
4 | import { PuzzleService } from './puzzle.service';
5 | import { PuzzleController } from './puzzle.controller';
6 |
7 | @Module({
8 | imports: [TypeOrmModule.forFeature([Puzzle])],
9 | controllers: [PuzzleController],
10 | providers: [PuzzleService],
11 | exports: [PuzzleService],
12 | })
13 | export class PuzzleModule {}
14 |
--------------------------------------------------------------------------------
/backend/src/content/content.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { TypeOrmModule } from '@nestjs/typeorm';
3 | import { ContentController } from './content.controller';
4 | import { ContentService } from './content.service';
5 | import { Content } from './content.entity';
6 |
7 | @Module({
8 | imports: [TypeOrmModule.forFeature([Content])],
9 | controllers: [ContentController],
10 | providers: [ContentService],
11 | exports: [ContentService],
12 | })
13 | export class ContentModule {}
--------------------------------------------------------------------------------
/backend/src/feedback/index.ts:
--------------------------------------------------------------------------------
1 | // Main exports for the feedback module
2 | export { FeedbackModule } from "./feedback.module"
3 | export { FeedbackService } from "./services/feedback.service"
4 | export { FeedbackController } from "./controllers/feedback.controller"
5 | export { AdminGuard } from "./guards/admin.guard"
6 | export { Feedback, TargetType } from "./entities/feedback.entity"
7 |
8 | // Interface and DTO exports
9 | export * from "./interfaces/feedback.interface"
10 | export * from "./dto/feedback.dto"
11 |
--------------------------------------------------------------------------------
/backend/src/nft-claim/dto/claim-nft.dto.ts:
--------------------------------------------------------------------------------
1 | import { ApiProperty } from '@nestjs/swagger';
2 | import { IsNotEmpty, IsString } from 'class-validator';
3 |
4 | export class ClaimNFTDto {
5 | @ApiProperty({ example: 'user123', description: 'Unique identifier of the user claiming the NFT' })
6 | @IsString()
7 | @IsNotEmpty()
8 | userId: string;
9 |
10 | @ApiProperty({ example: 'nft456', description: 'Unique identifier of the NFT to be claimed' })
11 | @IsString()
12 | @IsNotEmpty()
13 | nftId: string;
14 | }
--------------------------------------------------------------------------------
/backend/src/rate-limiter/rate-limiter.module.ts:
--------------------------------------------------------------------------------
1 | import { Module, DynamicModule } from '@nestjs/common';
2 | import { RateLimiterService } from './rate-limiter.service';
3 | import { RateLimitGuard } from './rate-limit.guard';
4 |
5 | @Module({})
6 | export class RateLimiterModule {
7 | static forRoot(): DynamicModule {
8 | return {
9 | module: RateLimiterModule,
10 | providers: [RateLimiterService, RateLimitGuard],
11 | exports: [RateLimiterService, RateLimitGuard],
12 | };
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/backend/src/quiz/dto/submit-quiz.dto.ts:
--------------------------------------------------------------------------------
1 | import { IsString, IsArray, IsOptional, IsNumber } from 'class-validator';
2 |
3 | export class SubmitAnswerDto {
4 | @IsString()
5 | questionId: string;
6 |
7 | @IsArray()
8 | @IsString({ each: true })
9 | selectedOptionIds: string[];
10 | }
11 |
12 | export class SubmitQuizDto {
13 | @IsString()
14 | quizId: string;
15 |
16 | @IsArray()
17 | answers: SubmitAnswerDto[];
18 |
19 | @IsOptional()
20 | @IsNumber()
21 | timeTaken?: number; // in seconds
22 | }
23 |
--------------------------------------------------------------------------------
/backend/config/database.config.ts:
--------------------------------------------------------------------------------
1 | import { registerAs } from '@nestjs/config';
2 |
3 | export default registerAs('database', () => ({
4 | host: process.env.DATABASE_HOST || 'localhost',
5 | port: parseInt(process.env.DATABASE_PORT) || 5432,
6 | user: process.env.DATABASE_USER,
7 | password: process.env.DATABASE_PASSWORD,
8 | name: process.env.DATABASE_NAME,
9 | synchronize: process.env.DATABASE_SYNC === 'true' ? 'true' : 'false',
10 | autoload: process.env.DATABASE_LOAD === 'true' ? 'true' : 'false',
11 | }));
12 |
--------------------------------------------------------------------------------
/frontend/components/BackToHome.jsx:
--------------------------------------------------------------------------------
1 | import { House } from "lucide-react"
2 | import Link from "next/link"
3 |
4 | const BackToHome = () => {
5 | return (
6 |
7 |
8 | Back to Home
9 |
10 |
11 |
12 | )
13 | }
14 |
15 | export default BackToHome
16 |
--------------------------------------------------------------------------------
/backend/src/activity/activity.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { ActivityService } from './activity.service';
3 | import { ActivityController } from './activity.controller';
4 | import { TypeOrmModule } from '@nestjs/typeorm';
5 | import { Activity } from './entities/activity.entity';
6 |
7 | @Module({
8 | imports: [TypeOrmModule.forFeature([Activity])],
9 | providers: [ActivityService],
10 | controllers: [ActivityController],
11 | exports: [ActivityService],
12 | })
13 | export class ActivityModule {}
14 |
--------------------------------------------------------------------------------
/backend/src/badge/badge.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { TypeOrmModule } from '@nestjs/typeorm';
3 | import { Badge } from './entities/badge.entity';
4 | import { UserBadge } from './entities/user-badge.entity';
5 | import { BadgeService } from './badge.service';
6 | import { BadgeController } from './badge.controller';
7 |
8 | @Module({
9 | imports: [TypeOrmModule.forFeature([Badge, UserBadge])],
10 | providers: [BadgeService],
11 | controllers: [BadgeController],
12 | })
13 | export class BadgeModule {}
14 |
--------------------------------------------------------------------------------
/backend/src/geostats/geostats.controller.ts:
--------------------------------------------------------------------------------
1 |
2 | import { Controller, Post, Get, Ip } from '@nestjs/common';
3 | import { GeoStatsService } from './geostats.service';
4 |
5 | @Controller('geostats')
6 | export class GeoStatsController {
7 | constructor(private readonly geoStatsService: GeoStatsService) {}
8 |
9 | @Post('track')
10 | track(@Ip() ip: string) {
11 |
12 | return this.geoStatsService.trackUser(ip);
13 | }
14 |
15 | @Get('stats')
16 | getStats() {
17 | return this.geoStatsService.getStats();
18 | }
19 | }
--------------------------------------------------------------------------------
/backend/src/audit-log/audit-log.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { TypeOrmModule } from '@nestjs/typeorm';
3 | import { AuditLog } from './entities/audit-log.entity';
4 | import { AuditLogService } from './audit-log.service';
5 | import { AuditLogController } from './audit-log.controller';
6 |
7 | @Module({
8 | imports: [TypeOrmModule.forFeature([AuditLog])],
9 | providers: [AuditLogService],
10 | controllers: [AuditLogController],
11 | exports: [AuditLogService],
12 | })
13 | export class AuditLogModule {}
14 |
--------------------------------------------------------------------------------
/backend/src/badge/entities/user-badge.entity.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Entity,
3 | PrimaryGeneratedColumn,
4 | ManyToOne,
5 | CreateDateColumn,
6 | Column,
7 | } from 'typeorm';
8 | import { Badge } from './badge.entity';
9 |
10 | @Entity('user_badges')
11 | export class UserBadge {
12 | @PrimaryGeneratedColumn()
13 | id: number;
14 |
15 | @Column()
16 | userId: number;
17 |
18 | @ManyToOne(() => Badge, (badge) => badge.userBadges, { eager: true })
19 | badge: Badge;
20 |
21 | @CreateDateColumn()
22 | awardedAt: Date;
23 | }
24 |
--------------------------------------------------------------------------------
/frontend/components.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://ui.shadcn.com/schema.json",
3 | "style": "new-york",
4 | "rsc": true,
5 | "tsx": false,
6 | "tailwind": {
7 | "config": "tailwind.config.js",
8 | "css": "app/globals.css",
9 | "baseColor": "zinc",
10 | "cssVariables": false,
11 | "prefix": ""
12 | },
13 | "aliases": {
14 | "components": "@/components",
15 | "utils": "@/lib/utils",
16 | "ui": "@/components/ui",
17 | "lib": "@/lib",
18 | "hooks": "@/hooks"
19 | },
20 | "iconLibrary": "lucide"
21 | }
--------------------------------------------------------------------------------
/backend/src/daily-reward/daily-reward.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { TypeOrmModule } from '@nestjs/typeorm';
3 | import { DailyRewardLog } from './entities/daily-reward-log.entity';
4 | import { DailyRewardService } from './daily-reward.service';
5 | import { DailyRewardController } from './daily-reward.controller';
6 |
7 | @Module({
8 | imports: [TypeOrmModule.forFeature([DailyRewardLog])],
9 | providers: [DailyRewardService],
10 | controllers: [DailyRewardController],
11 | })
12 | export class DailyRewardModule {}
--------------------------------------------------------------------------------
/backend/src/geostats/geostats.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { TypeOrmModule } from '@nestjs/typeorm';
3 | import { HttpModule } from '@nestjs/axios';
4 | import { GeoStats } from './entities/geostat.entity';
5 | import { GeoStatsService } from './geostats.service';
6 | import { GeoStatsController } from './geostats.controller';
7 |
8 | @Module({
9 | imports: [TypeOrmModule.forFeature([GeoStats]), HttpModule],
10 | providers: [GeoStatsService],
11 | controllers: [GeoStatsController],
12 | })
13 | export class GeoStatsModule {}
--------------------------------------------------------------------------------
/backend/src/time-trial/time-trial.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { TimetrialService } from './providers/timetrial.service';
3 | import { TimeTrialController } from './time-trial.controller';
4 | import { TypeOrmModule } from '@nestjs/typeorm';
5 | import { TimeTrial } from './time-trial.entity';
6 |
7 | @Module({
8 | imports:[TypeOrmModule.forFeature([TimeTrial])],
9 | providers: [TimetrialService],
10 | controllers: [TimeTrialController],
11 | exports: [TimetrialService]
12 | })
13 | export class TimeTrialModule {}
14 |
--------------------------------------------------------------------------------
/backend/src/nft-marketplace-stub/nft-marketplace-stub.controller.ts:
--------------------------------------------------------------------------------
1 | import { Controller, Get } from '@nestjs/common';
2 | import { NftMarketplaceStubService } from './nft-marketplace-stub.service';
3 | import { NftItem } from './entities/nft-item.entity';
4 |
5 | @Controller('nft-marketplace')
6 | export class NftMarketplaceStubController {
7 | constructor(
8 | private readonly marketplaceService: NftMarketplaceStubService,
9 | ) {}
10 |
11 | @Get('list')
12 | listAllNfts(): NftItem[] {
13 | return this.marketplaceService.findAll();
14 | }
15 | }
--------------------------------------------------------------------------------
/backend/src/promo-code/entities/promo-code.entities.ts:
--------------------------------------------------------------------------------
1 | // src/promo-code/entities/promo-code.entity.ts
2 | import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn } from 'typeorm';
3 |
4 | @Entity('promo_codes')
5 | export class PromoCode {
6 | @PrimaryGeneratedColumn('uuid')
7 | id: string;
8 |
9 | @Column({ unique: true })
10 | code: string;
11 |
12 | @Column({ nullable: true })
13 | description: string;
14 |
15 | @Column({ type: 'timestamp' })
16 | expiresAt: Date;
17 |
18 | @CreateDateColumn()
19 | createdAt: Date;
20 | }
21 |
--------------------------------------------------------------------------------
/backend/src/quiz/quiz.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { Test, TestingModule } from '@nestjs/testing';
2 | import { QuizService } from './services/quiz.service';
3 |
4 | describe('QuizService', () => {
5 | let service: QuizService;
6 |
7 | beforeEach(async () => {
8 | const module: TestingModule = await Test.createTestingModule({
9 | providers: [QuizService],
10 | }).compile();
11 |
12 | service = module.get(QuizService);
13 | });
14 |
15 | it('should be defined', () => {
16 | expect(service).toBeDefined();
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/backend/src/migration/index.ts:
--------------------------------------------------------------------------------
1 | // Main exports for the migration module
2 | export { MigrationModule } from "./migration.module"
3 | export { JsonParserService } from "./services/json-parser.service"
4 | export { MigrationService } from "./services/migration.service"
5 | export { MigrationController } from "./controllers/migration.controller"
6 | export { AdminGuard } from "./guards/admin.guard"
7 | export { Puzzle } from "./entities/puzzle.entity"
8 |
9 | // Interface exports
10 | export * from "./interfaces/puzzle.interface"
11 | export * from "./dto/migration.dto"
12 |
--------------------------------------------------------------------------------
/backend/src/puzzle-draft/puzzle-draft.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { TypeOrmModule } from '@nestjs/typeorm';
3 | import { DraftPuzzle } from './entities/draft-puzzle.entity';
4 | import { DraftPuzzleService } from './draft-puzzle.service';
5 | import { DraftPuzzleController } from './draft-puzzle.controller';
6 |
7 | @Module({
8 | imports: [TypeOrmModule.forFeature([DraftPuzzle])],
9 | controllers: [DraftPuzzleController],
10 | providers: [DraftPuzzleService],
11 | exports: [],
12 | })
13 | export class PuzzleDraftModule {}
14 |
--------------------------------------------------------------------------------
/backend/src/content-rating/content-rating.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { TypeOrmModule } from '@nestjs/typeorm';
3 | import { ContentRating } from './entities/content-rating.entity';
4 | import { ContentRatingService } from './content-rating.service';
5 | import { ContentRatingController } from './content-rating.controller';
6 |
7 | @Module({
8 | imports: [TypeOrmModule.forFeature([ContentRating])],
9 | controllers: [ContentRatingController],
10 | providers: [ContentRatingService],
11 | })
12 | export class ContentRatingModule {}
13 |
--------------------------------------------------------------------------------
/backend/src/user-ranking/user-ranking.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { TypeOrmModule } from '@nestjs/typeorm';
3 | import { UserRankingController } from './user-ranking.controller';
4 | import { UserRankingService } from './user-ranking.service';
5 | import { UserRank } from './entities/user-ranking.entity';
6 |
7 | @Module({
8 | imports: [TypeOrmModule.forFeature([UserRank])],
9 | controllers: [UserRankingController],
10 | providers: [UserRankingService],
11 | exports: [UserRankingService],
12 | })
13 | export class UserRankingModule {}
--------------------------------------------------------------------------------
/frontend/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 | .yarn/install-state.gz
8 |
9 | # testing
10 | /coverage
11 |
12 | # next.js
13 | /.next/
14 | /out/
15 |
16 | # production
17 | /build
18 |
19 | # misc
20 | .DS_Store
21 | *.pem
22 |
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
28 | # local env files
29 | .env*.local
30 |
31 | # vercel
32 | .vercel
33 |
34 | # typescript
35 | *.tsbuildinfo
36 | next-env.d.ts
37 |
--------------------------------------------------------------------------------
/backend/src/api-key/api-key.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { Test, TestingModule } from '@nestjs/testing';
2 | import { ApiKeyService } from './api-key.service';
3 |
4 | describe('ApiKeyService', () => {
5 | let service: ApiKeyService;
6 |
7 | beforeEach(async () => {
8 | const module: TestingModule = await Test.createTestingModule({
9 | providers: [ApiKeyService],
10 | }).compile();
11 |
12 | service = module.get(ApiKeyService);
13 | });
14 |
15 | it('should be defined', () => {
16 | expect(service).toBeDefined();
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/onchain/Scarb.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "onchain"
3 | version = "0.1.0"
4 | edition = "2023_11"
5 |
6 | # See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html
7 |
8 | [dependencies]
9 | starknet = "2.8.4"
10 | openzeppelin = { git = "https://github.com/OpenZeppelin/cairo-contracts.git", tag = "v0.19.0" }
11 |
12 | [dev-dependencies]
13 | snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry", tag = "v0.30.0" }
14 |
15 | [[target.starknet-contract]]
16 | sierra = true
17 |
18 | [scripts]
19 | test = "snforge test"
--------------------------------------------------------------------------------
/onchain/src/utils.cairo:
--------------------------------------------------------------------------------
1 | use core::array::ArrayTrait;
2 | use core::byte_array::ByteArray;
3 | use core::felt252;
4 | use core::poseidon::poseidon_hash_span;
5 |
6 | pub fn hash_byte_array(byte_array: ByteArray) -> felt252 {
7 | let mut felt_array: Array = ArrayTrait::new();
8 | let len = byte_array.len();
9 | let mut i: usize = 0;
10 |
11 | while i != len {
12 | let byte = byte_array.at(i).unwrap();
13 | felt_array.append(byte.into());
14 | i += 1;
15 | };
16 |
17 | poseidon_hash_span(felt_array.span())
18 | }
19 |
20 |
--------------------------------------------------------------------------------
/backend/src/auth/dto/login.dto.ts:
--------------------------------------------------------------------------------
1 | import { ApiProperty } from "@nestjs/swagger"
2 | import { IsEmail, IsString, IsNotEmpty } from "class-validator"
3 |
4 | export class LoginDto {
5 | @ApiProperty({
6 | description: "User email address",
7 | example: "john.doe@example.com",
8 | })
9 | @IsEmail({}, { message: "Please provide a valid email address" })
10 | @IsNotEmpty()
11 | email: string
12 |
13 | @ApiProperty({
14 | description: "User password",
15 | example: "SecurePass123!",
16 | })
17 | @IsString()
18 | @IsNotEmpty()
19 | password: string
20 | }
21 |
--------------------------------------------------------------------------------
/backend/src/puzzle-access-log/puzzle-access-log.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { TypeOrmModule } from '@nestjs/typeorm';
3 | import { PuzzleAccessLog } from './entities/puzzle-access-log.entity';
4 | import { PuzzleAccessLogService } from './puzzle-access-log.service';
5 | import { PuzzleAccessLogController } from './puzzle-access-log.controller';
6 |
7 | @Module({
8 | imports: [TypeOrmModule.forFeature([PuzzleAccessLog])],
9 | providers: [PuzzleAccessLogService],
10 | controllers: [PuzzleAccessLogController],
11 | })
12 | export class PuzzleAccessLogModule {}
--------------------------------------------------------------------------------
/backend/src/puzzle-versioning/puzzle-versioning.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { TypeOrmModule } from '@nestjs/typeorm';
3 | import { PuzzleVersion } from './entities/puzzle-version.entity';
4 | import { PuzzleVersioningService } from './puzzle-versioning.service';
5 | import { PuzzleVersioningController } from './puzzle-versioning.controller';
6 |
7 | @Module({
8 | imports: [TypeOrmModule.forFeature([PuzzleVersion])],
9 | providers: [PuzzleVersioningService],
10 | controllers: [PuzzleVersioningController],
11 | })
12 | export class PuzzleVersioningModule {}
--------------------------------------------------------------------------------
/backend/src/rewards/dto/claim-reward.dto.ts:
--------------------------------------------------------------------------------
1 | import { ApiProperty } from '@nestjs/swagger';
2 | import { IsString, IsNotEmpty } from 'class-validator';
3 |
4 | export class ClaimRewardDto {
5 | @ApiProperty({
6 | description: 'User ID requesting the reward claim',
7 | example: 'user-123',
8 | })
9 | @IsString()
10 | @IsNotEmpty()
11 | userId: string;
12 |
13 | @ApiProperty({
14 | description: 'Challenge ID for which the reward is being claimed',
15 | example: 'challenge-easy-001',
16 | })
17 | @IsString()
18 | @IsNotEmpty()
19 | challengeId: string;
20 | }
21 |
--------------------------------------------------------------------------------
/backend/src/content/content.entity.ts:
--------------------------------------------------------------------------------
1 | import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from 'typeorm';
2 |
3 | @Entity('contents')
4 | export class Content {
5 | @PrimaryGeneratedColumn('uuid')
6 | id: string;
7 |
8 | @Column({ length: 255 })
9 | title: string;
10 |
11 | @Column('text')
12 | body: string;
13 |
14 | @Column({ length: 100 })
15 | topic: string;
16 |
17 | @Column({ default: true })
18 | isActive: boolean;
19 |
20 | @CreateDateColumn()
21 | createdAt: Date;
22 |
23 | @UpdateDateColumn()
24 | updatedAt: Date;
25 | }
--------------------------------------------------------------------------------
/backend/src/puzzle-access-log/entities/puzzle-access-log.entity.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Entity,
3 | PrimaryGeneratedColumn,
4 | Column,
5 | CreateDateColumn,
6 | Index,
7 | } from 'typeorm';
8 |
9 | @Entity('puzzle_access_logs')
10 | export class PuzzleAccessLog {
11 | @PrimaryGeneratedColumn('uuid')
12 | id: string;
13 |
14 | @Index() // Index for faster user-specific queries
15 | @Column()
16 | userId: string;
17 |
18 | @Index() // Index for faster puzzle-specific queries
19 | @Column()
20 | puzzleId: string;
21 |
22 | @CreateDateColumn()
23 | accessTimestamp: Date;
24 | }
--------------------------------------------------------------------------------
/backend/src/analytics/analytics.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { Test, TestingModule } from '@nestjs/testing';
2 | import { AnalyticsService } from './analytics.service';
3 |
4 | describe('AnalyticsService', () => {
5 | let service: AnalyticsService;
6 |
7 | beforeEach(async () => {
8 | const module: TestingModule = await Test.createTestingModule({
9 | providers: [AnalyticsService],
10 | }).compile();
11 |
12 | service = module.get(AnalyticsService);
13 | });
14 |
15 | it('should be defined', () => {
16 | expect(service).toBeDefined();
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/backend/src/session/session.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { TypeOrmModule } from '@nestjs/typeorm';
3 | import { ScheduleModule } from '@nestjs/schedule';
4 | import { Session } from './session.entity';
5 | import { SessionService } from './session.service';
6 | import { SessionController } from './session.controller';
7 |
8 | @Module({
9 | imports: [TypeOrmModule.forFeature([Session]), ScheduleModule.forRoot()],
10 | providers: [SessionService],
11 | controllers: [SessionController],
12 | exports: [SessionService],
13 | })
14 | export class SessionModule {}
15 |
--------------------------------------------------------------------------------
/backend/src/gameMechanics/guards/admin.guard.ts:
--------------------------------------------------------------------------------
1 | import { Injectable, type CanActivate, type ExecutionContext, ForbiddenException } from "@nestjs/common"
2 | import { UserRole } from "../entities/user.entity"
3 |
4 | @Injectable()
5 | export class AdminGuard implements CanActivate {
6 | canActivate(context: ExecutionContext): boolean {
7 | const request = context.switchToHttp().getRequest()
8 | const user = request.user
9 |
10 | if (!user || user.role !== UserRole.ADMIN) {
11 | throw new ForbiddenException("Admin access required")
12 | }
13 |
14 | return true
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/backend/src/reward-shop/reward-shop.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { Test, TestingModule } from '@nestjs/testing';
2 | import { RewardShopService } from './reward-shop.service';
3 |
4 | describe('RewardShopService', () => {
5 | let service: RewardShopService;
6 |
7 | beforeEach(async () => {
8 | const module: TestingModule = await Test.createTestingModule({
9 | providers: [RewardShopService],
10 | }).compile();
11 |
12 | service = module.get(RewardShopService);
13 | });
14 |
15 | it('should be defined', () => {
16 | expect(service).toBeDefined();
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/backend/src/user-token-history/index.ts:
--------------------------------------------------------------------------------
1 | // Main exports for the user token history module
2 | export { UserTokenHistoryModule } from "./user-token-history.module"
3 | export { UserTokenHistoryService } from "./services/user-token-history.service"
4 | export { TokenHistoryController } from "./controllers/token-history.controller"
5 | export { AdminGuard } from "./guards/admin.guard"
6 | export { TokenHistory, TokenType, TokenStatus } from "./entities/token-history.entity"
7 |
8 | // Interface and DTO exports
9 | export * from "./interfaces/token-history.interface"
10 | export * from "./dto/token-history.dto"
11 |
--------------------------------------------------------------------------------
/backend/src/puzzle-submission/puzzle-submission.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { TypeOrmModule } from '@nestjs/typeorm';
3 | import { PuzzleSubmission } from './puzzle-submission.entity';
4 | import { PuzzleSubmissionService } from '../puzzle-submission/puzzle-submission.service';
5 | import { PuzzleSubmissionController } from './puzzle-submission.controller';
6 |
7 | @Module({
8 | imports: [TypeOrmModule.forFeature([PuzzleSubmission])],
9 | providers: [PuzzleSubmissionService],
10 | controllers: [PuzzleSubmissionController],
11 | })
12 | export class PuzzleSubmissionModule {}
13 |
--------------------------------------------------------------------------------
/backend/src/quiz/dto/quiz-result.dto.ts:
--------------------------------------------------------------------------------
1 | export class QuestionResultDto {
2 | questionId: string;
3 | question: string;
4 | selectedOptions: string[];
5 | correctOptions: string[];
6 | isCorrect: boolean;
7 | points: number;
8 | earnedPoints: number;
9 | explanation?: string;
10 | }
11 |
12 | export class QuizResultDto {
13 | quizId: string;
14 | quizTitle: string;
15 | totalQuestions: number;
16 | totalPoints: number;
17 | earnedPoints: number;
18 | percentage: number;
19 | passed: boolean;
20 | timeTaken?: number;
21 | answers: QuestionResultDto[];
22 | completedAt: Date;
23 | }
24 |
--------------------------------------------------------------------------------
/backend/src/rewards/rewards.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { TypeOrmModule } from '@nestjs/typeorm';
3 | import { RewardsController } from './rewards.controller';
4 | import { RewardsService } from './rewards.service';
5 | import { Reward } from './entities/reward.entity';
6 | import { RewardClaim } from './entities/reward-claim.entity';
7 |
8 | @Module({
9 | imports: [
10 | TypeOrmModule.forFeature([Reward, RewardClaim]),
11 | ],
12 | controllers: [RewardsController],
13 | providers: [RewardsService],
14 | exports: [RewardsService],
15 | })
16 | export class RewardsModule {}
--------------------------------------------------------------------------------
/backend/src/achievements/achievements.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { Test, TestingModule } from '@nestjs/testing';
2 | import { AchievementService } from './achievements.service';
3 |
4 | describe('AchievementsService', () => {
5 | let service: AchievementService;
6 |
7 | beforeEach(async () => {
8 | const module: TestingModule = await Test.createTestingModule({
9 | providers: [AchievementService],
10 | }).compile();
11 |
12 | service = module.get(AchievementService);
13 | });
14 |
15 | it('should be defined', () => {
16 | expect(service).toBeDefined();
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/backend/src/admin/admin.entity.ts:
--------------------------------------------------------------------------------
1 | import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from 'typeorm';
2 | import { AdminRole } from './admin-role.enum';
3 |
4 | @Entity('admins')
5 | export class Admin {
6 | @PrimaryGeneratedColumn('uuid')
7 | id: string;
8 |
9 | @Column({ unique: true })
10 | email: string;
11 |
12 | @Column()
13 | password: string;
14 |
15 | @Column({ type: 'enum', enum: AdminRole, default: AdminRole.ADMIN })
16 | role: AdminRole;
17 |
18 | @CreateDateColumn()
19 | createdAt: Date;
20 |
21 | @UpdateDateColumn()
22 | updatedAt: Date;
23 | }
24 |
--------------------------------------------------------------------------------
/backend/src/puzzle-draft/entities/draft-puzzle.entity.ts:
--------------------------------------------------------------------------------
1 | import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from 'typeorm';
2 |
3 | @Entity('draft_puzzles')
4 | export class DraftPuzzle {
5 | @PrimaryGeneratedColumn('uuid')
6 | id: string;
7 |
8 | @Column()
9 | title: string;
10 |
11 | @Column({ nullable: true })
12 | description: string;
13 |
14 | @Column('jsonb')
15 | content: Record;
16 |
17 | @Column()
18 | createdBy: string;
19 |
20 | @CreateDateColumn()
21 | createdAt: Date;
22 |
23 | @UpdateDateColumn()
24 | updatedAt: Date;
25 | }
26 |
--------------------------------------------------------------------------------
/backend/src/puzzle-submission/puzzle-submission.entity.ts:
--------------------------------------------------------------------------------
1 | import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, Index } from 'typeorm';
2 |
3 | @Entity()
4 | @Index(['playerId', 'puzzleId'])
5 | export class PuzzleSubmission {
6 | @PrimaryGeneratedColumn()
7 | id: number;
8 |
9 | @Column()
10 | playerId: string;
11 |
12 | @Column()
13 | puzzleId: string;
14 |
15 | @Column()
16 | answer: string;
17 |
18 | @Column({ default: false })
19 | isCorrect: boolean;
20 |
21 | @Column({ default: 1 })
22 | attemptCount: number;
23 |
24 | @CreateDateColumn()
25 | timestamp: Date;
26 | }
27 |
--------------------------------------------------------------------------------
/backend/src/user-ranking/user-ranking.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { Test, TestingModule } from '@nestjs/testing';
2 | import { UserRankingService } from './user-ranking.service';
3 |
4 | describe('UserRankingService', () => {
5 | let service: UserRankingService;
6 |
7 | beforeEach(async () => {
8 | const module: TestingModule = await Test.createTestingModule({
9 | providers: [UserRankingService],
10 | }).compile();
11 |
12 | service = module.get(UserRankingService);
13 | });
14 |
15 | it('should be defined', () => {
16 | expect(service).toBeDefined();
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/frontend/app/puzzles/roadmap/app.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import PuzzleTimeline from '@/components/puzzles/roadmap/PuzzleTimeline';
4 | import { puzzleRoadmap } from '@/components/puzzles/roadmap/puzzleRoadmapData';
5 |
6 | export default function PuzzleRoadmapPage() {
7 | return (
8 |
9 |
10 | Puzzle Roadmap
11 |
12 |
13 |
14 | );
15 | }
16 |
--------------------------------------------------------------------------------
/backend/src/user-activity-log/user-activity-log.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { TypeOrmModule } from '@nestjs/typeorm';
3 | import { ActivityLog } from './entities/activity-log.entity';
4 | import { UserActivityLogService } from './user-activity-log.service';
5 | import { UserActivityLogController } from './user-activity-log.controller';
6 |
7 | @Module({
8 | imports: [TypeOrmModule.forFeature([ActivityLog])],
9 | controllers: [UserActivityLogController],
10 | providers: [UserActivityLogService],
11 | exports: [UserActivityLogService]
12 | })
13 | export class UserActivityLogModule {}
14 |
--------------------------------------------------------------------------------
/backend/src/streak/dto/record-activity.dto.ts:
--------------------------------------------------------------------------------
1 | import { IsEnum, IsOptional, IsString, IsInt, Min, IsDateString } from "class-validator"
2 | import { ActivityType } from "../entities/streak-activity.entity"
3 |
4 | export class RecordActivityDto {
5 | @IsEnum(ActivityType)
6 | activityType: ActivityType
7 |
8 | @IsOptional()
9 | @IsDateString()
10 | activityDate?: string // If not provided, uses current date
11 |
12 | @IsOptional()
13 | @IsInt()
14 | @Min(1)
15 | activityCount?: number
16 |
17 | @IsOptional()
18 | @IsString()
19 | description?: string
20 |
21 | @IsOptional()
22 | metadata?: any
23 | }
24 |
--------------------------------------------------------------------------------
/backend/src/user-reaction/dto/reaction-aggregation.dto.ts:
--------------------------------------------------------------------------------
1 | import { ApiProperty } from "@nestjs/swagger"
2 |
3 | export class ReactionAggregationDto {
4 | @ApiProperty({
5 | description: "Content ID",
6 | example: "puzzle-123",
7 | })
8 | contentId: string
9 |
10 | @ApiProperty({
11 | description: "Reaction counts per emoji",
12 | example: {
13 | "👍": 15,
14 | "❤️": 8,
15 | "🤔": 3,
16 | },
17 | })
18 | reactions: Record
19 |
20 | @ApiProperty({
21 | description: "Total reaction count",
22 | example: 26,
23 | })
24 | totalReactions: number
25 | }
26 |
--------------------------------------------------------------------------------
/backend/src/user-reaction/user-reaction.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from "@nestjs/common"
2 | import { TypeOrmModule } from "@nestjs/typeorm"
3 | import { UserReactionService } from "./user-reaction.service"
4 | import { UserReactionController } from "./user-reaction.controller"
5 | import { Reaction } from "./entities/reaction.entity"
6 |
7 | @Module({
8 | imports: [TypeOrmModule.forFeature([Reaction])],
9 | controllers: [UserReactionController],
10 | providers: [UserReactionService],
11 | exports: [UserReactionService], // Export service for potential use in other modules
12 | })
13 | export class UserReactionModule {}
14 |
--------------------------------------------------------------------------------
/frontend/components/general/FeatureCard.jsx:
--------------------------------------------------------------------------------
1 | import { Card, CardContent } from "@/components/ui/card";
2 |
3 | const FeatureCard = ({ icon, title, description }) => (
4 |
5 |
6 |
7 | {icon}
8 |
{title}
9 |
{description}
10 |
11 |
12 |
13 | );
14 |
15 | export default FeatureCard;
16 |
--------------------------------------------------------------------------------
/backend/src/content-rating/content-rating.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { Test, TestingModule } from '@nestjs/testing';
2 | import { ContentRatingService } from './content-rating.service';
3 |
4 | describe('ContentRatingService', () => {
5 | let service: ContentRatingService;
6 |
7 | beforeEach(async () => {
8 | const module: TestingModule = await Test.createTestingModule({
9 | providers: [ContentRatingService],
10 | }).compile();
11 |
12 | service = module.get(ContentRatingService);
13 | });
14 |
15 | it('should be defined', () => {
16 | expect(service).toBeDefined();
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/backend/src/puzzle-comment/puzzle-comment.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { Test, TestingModule } from '@nestjs/testing';
2 | import { PuzzleCommentService } from './puzzle-comment.service';
3 |
4 | describe('PuzzleCommentService', () => {
5 | let service: PuzzleCommentService;
6 |
7 | beforeEach(async () => {
8 | const module: TestingModule = await Test.createTestingModule({
9 | providers: [PuzzleCommentService],
10 | }).compile();
11 |
12 | service = module.get(PuzzleCommentService);
13 | });
14 |
15 | it('should be defined', () => {
16 | expect(service).toBeDefined();
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/backend/src/user-inventory/dto/add-inventory-item.ts:
--------------------------------------------------------------------------------
1 | import { ApiProperty } from '@nestjs/swagger';
2 | import { IsUUID, IsEnum, IsOptional, IsObject } from 'class-validator';
3 | import { AssetType } from '../entities/inventory';
4 |
5 | export class AddInventoryItemDto {
6 | @ApiProperty()
7 | @IsUUID()
8 | userId: string;
9 |
10 | @ApiProperty()
11 | @IsUUID()
12 | assetId: string;
13 |
14 | @ApiProperty({ enum: AssetType })
15 | @IsEnum(AssetType)
16 | assetType: AssetType;
17 |
18 | @ApiProperty({ required: false })
19 | @IsOptional()
20 | @IsObject()
21 | acquisitionContext?: Record;
22 | }
--------------------------------------------------------------------------------
/frontend/components/ui/LoadingSpinner.jsx:
--------------------------------------------------------------------------------
1 | import { cn } from "../../lib/utils";
2 |
3 | const LoadingSpinner = ({ className, size = "md" }) => {
4 | const sizeClasses = {
5 | sm: "h-4 w-4",
6 | md: "h-8 w-8",
7 | lg: "h-12 w-12",
8 | xl: "h-16 w-16",
9 | };
10 |
11 | return (
12 |
20 | );
21 | };
22 |
23 | export default LoadingSpinner;
--------------------------------------------------------------------------------
/frontend/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/backend/config/app.config.ts:
--------------------------------------------------------------------------------
1 | import { registerAs } from '@nestjs/config';
2 |
3 | export default registerAs('appConfig', () => ({
4 | environment: process.env.NODE_ENV || 'development',
5 | apiVersion: process.env.API_VERSION,
6 | cors: {
7 | origin: process.env.FRONTEND_URL || 'http://localhost:3000',
8 | methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
9 | allowedHeaders: [
10 | 'Origin',
11 | 'X-Requested-With',
12 | 'Content-Type',
13 | 'Accept',
14 | 'Authorization',
15 | ],
16 | credentials: true,
17 | }
18 | }));
19 |
--------------------------------------------------------------------------------
/backend/src/badge/badge.controller.ts:
--------------------------------------------------------------------------------
1 | import { Controller, Get, Param, Post, Body } from '@nestjs/common';
2 | import { BadgeService } from './badge.service';
3 | import { AssignBadgeDto } from './dto/assign-badge.dto';
4 |
5 | @Controller('badges')
6 | export class BadgeController {
7 | constructor(private readonly badgeService: BadgeService) {}
8 |
9 | @Post('assign')
10 | assignBadge(@Body() dto: AssignBadgeDto) {
11 | return this.badgeService.assignBadgeToUser(dto);
12 | }
13 |
14 | @Get('user/:id')
15 | getUserBadges(@Param('id') id: string) {
16 | return this.badgeService.getBadgesForUser(+id);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/backend/src/user-settings/user-settings.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from "@nestjs/common"
2 | import { TypeOrmModule } from "@nestjs/typeorm"
3 | import { UserSettingsService } from "./user-settings.service"
4 | import { UserSettingsController } from "./user-settings.controller"
5 | import { UserSettings } from "./entities/user-settings.entity"
6 |
7 | @Module({
8 | imports: [TypeOrmModule.forFeature([UserSettings])],
9 | controllers: [UserSettingsController],
10 | providers: [UserSettingsService],
11 | exports: [UserSettingsService], // Export service for potential use in other modules
12 | })
13 | export class UserSettingsModule {}
14 |
--------------------------------------------------------------------------------
/backend/src/daily-reward/daily-reward.controller.ts:
--------------------------------------------------------------------------------
1 | import { Controller, Post, Body, UsePipes, ValidationPipe } from '@nestjs/common';
2 | import { DailyRewardService } from './daily-reward.service';
3 | import { DailyCheckinDto } from './dto/daily-checkin.dto';
4 |
5 | @Controller('rewards')
6 | export class DailyRewardController {
7 | constructor(private readonly dailyRewardService: DailyRewardService) {}
8 |
9 | @Post('daily-checkin')
10 | @UsePipes(new ValidationPipe({ transform: true }))
11 | dailyCheckIn(@Body() dailyCheckinDto: DailyCheckinDto) {
12 | return this.dailyRewardService.dailyCheckIn(dailyCheckinDto.userId);
13 | }
14 | }
--------------------------------------------------------------------------------
/backend/src/user-report-card/user-report-card.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { Test, TestingModule } from '@nestjs/testing';
2 | import { UserReportCardService } from './user-report-card.service';
3 |
4 | describe('UserReportCardService', () => {
5 | let service: UserReportCardService;
6 |
7 | beforeEach(async () => {
8 | const module: TestingModule = await Test.createTestingModule({
9 | providers: [UserReportCardService],
10 | }).compile();
11 |
12 | service = module.get(UserReportCardService);
13 | });
14 |
15 | it('should be defined', () => {
16 | expect(service).toBeDefined();
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/backend/src/badge/entities/badge.entity.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Entity,
3 | PrimaryGeneratedColumn,
4 | Column,
5 | CreateDateColumn,
6 | OneToMany,
7 | } from 'typeorm';
8 | import { UserBadge } from './user-badge.entity';
9 |
10 | @Entity('badges')
11 | export class Badge {
12 | @PrimaryGeneratedColumn()
13 | id: number;
14 |
15 | @Column()
16 | title: string;
17 |
18 | @Column({ nullable: true })
19 | description: string;
20 |
21 | @Column()
22 | iconUrl: string;
23 |
24 | @CreateDateColumn()
25 | createdAt: Date;
26 |
27 | @OneToMany(() => UserBadge, (userBadge) => userBadge.badge)
28 | userBadges: UserBadge[];
29 | }
30 |
--------------------------------------------------------------------------------
/backend/src/puzzle-fork/puzzle-fork.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { TypeOrmModule } from '@nestjs/typeorm';
3 | import { PuzzleForkService } from './puzzle-fork.service';
4 | import { PuzzleForkController } from './puzzle-fork.controller';
5 | import { ForkedPuzzle } from './entities/forked-puzzle.entity';
6 | import { PuzzleVersion } from '../puzzle-versioning/entities/puzzle-version.entity';
7 |
8 | @Module({
9 | imports: [
10 | TypeOrmModule.forFeature([ForkedPuzzle, PuzzleVersion]),
11 | ],
12 | controllers: [PuzzleForkController],
13 | providers: [PuzzleForkService],
14 | })
15 | export class PuzzleForkModule {}
--------------------------------------------------------------------------------
/backend/src/puzzle-translation/puzzle-translation.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { TypeOrmModule } from '@nestjs/typeorm';
3 | import { PuzzleTranslation } from './entities/puzzle-translation.entity';
4 | import { PuzzleTranslationService } from './puzzle-translation.service';
5 | import { PuzzleTranslationController } from './puzzle-translation.controller';
6 |
7 | @Module({
8 | imports: [TypeOrmModule.forFeature([PuzzleTranslation])],
9 | providers: [PuzzleTranslationService],
10 | controllers: [PuzzleTranslationController],
11 | exports: [PuzzleTranslationService],
12 | })
13 | export class PuzzleTranslationModule {}
--------------------------------------------------------------------------------
/backend/src/puzzle-versioning/entities/puzzle-version.entity.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Entity,
3 | PrimaryGeneratedColumn,
4 | Column,
5 | CreateDateColumn,
6 | Index,
7 | } from 'typeorm';
8 |
9 | @Entity('puzzle_versions')
10 | @Index(['puzzleId', 'version'], { unique: true })
11 | export class PuzzleVersion {
12 | @PrimaryGeneratedColumn('uuid')
13 | id: string;
14 |
15 |
16 | @Column()
17 | puzzleId: string;
18 |
19 | @Column()
20 | version: number;
21 |
22 | @Column()
23 | title: string;
24 |
25 |
26 | @Column({ type: 'jsonb' })
27 | content: Record;
28 |
29 | @CreateDateColumn()
30 | createdAt: Date;
31 | }
--------------------------------------------------------------------------------
/backend/src/session/session.entity.ts:
--------------------------------------------------------------------------------
1 | import { Entity, PrimaryGeneratedColumn, Column, Index } from 'typeorm';
2 | import { ActivityType } from './enum/activityType.enum';
3 |
4 | @Entity('sessions')
5 | export class Session {
6 | @PrimaryGeneratedColumn('uuid')
7 | sessionId: string;
8 |
9 | @Column({ type: 'uuid' })
10 | @Index()
11 | userId: string;
12 |
13 | @Column({ type: 'enum', enum: ActivityType })
14 | activityType: ActivityType;
15 |
16 | @Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' })
17 | startedAt: Date;
18 |
19 | @Column({ type: 'timestamp', nullable: true })
20 | endedAt: Date | null;
21 | }
22 |
--------------------------------------------------------------------------------
/backend/src/token-verification/decorators/token-payload.decorator.ts:
--------------------------------------------------------------------------------
1 | import { createParamDecorator, type ExecutionContext } from "@nestjs/common"
2 | import type { JwtPayload, WalletTokenPayload } from "../interfaces/token.interface"
3 |
4 | export const TokenPayload = createParamDecorator((data: unknown, ctx: ExecutionContext): JwtPayload => {
5 | const request = ctx.switchToHttp().getRequest()
6 | return request.tokenPayload
7 | })
8 |
9 | export const WalletPayload = createParamDecorator((data: unknown, ctx: ExecutionContext): WalletTokenPayload => {
10 | const request = ctx.switchToHttp().getRequest()
11 | return request.walletPayload
12 | })
13 |
--------------------------------------------------------------------------------
/backend/src/milestone/controllers/user-milestone.controller.ts:
--------------------------------------------------------------------------------
1 | import { Controller, Get } from "@nestjs/common"
2 | import type { MilestoneService } from "../services/milestone.service"
3 |
4 | @Controller("users")
5 | export class UserMilestoneController {
6 | constructor(private readonly milestoneService: MilestoneService) {}
7 |
8 | @Get(":id/milestones")
9 | async getUserMilestones(userId: string) {
10 | return this.milestoneService.getUserMilestones(userId)
11 | }
12 |
13 | @Get(":id/milestones/stats")
14 | async getUserMilestoneStats(userId: string) {
15 | return this.milestoneService.getUserMilestoneStats(userId)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/backend/src/puzzle-translation/entities/puzzle-translation.entity.ts:
--------------------------------------------------------------------------------
1 | import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, Unique, Index } from 'typeorm';
2 | import { Puzzle } from '../../puzzle/puzzle.entity';
3 |
4 | @Entity('puzzle_translations')
5 | @Unique(['puzzle', 'language'])
6 | export class PuzzleTranslation {
7 | @PrimaryGeneratedColumn()
8 | id: number;
9 |
10 | @ManyToOne(() => Puzzle, { onDelete: 'CASCADE' })
11 | puzzle: Puzzle;
12 |
13 | @Column()
14 | @Index()
15 | language: string; // e.g., 'en', 'es', 'fr'
16 |
17 | @Column('text')
18 | title: string;
19 |
20 | @Column('text')
21 | description: string;
22 | }
--------------------------------------------------------------------------------
/backend/src/user-activity-log/user-activity-log.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { Test, TestingModule } from '@nestjs/testing';
2 | import { UserActivityLogService } from './user-activity-log.service';
3 |
4 | describe('UserActivityLogService', () => {
5 | let service: UserActivityLogService;
6 |
7 | beforeEach(async () => {
8 | const module: TestingModule = await Test.createTestingModule({
9 | providers: [UserActivityLogService],
10 | }).compile();
11 |
12 | service = module.get(UserActivityLogService);
13 | });
14 |
15 | it('should be defined', () => {
16 | expect(service).toBeDefined();
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/backend/src/app.controller.ts:
--------------------------------------------------------------------------------
1 | import { Controller, Get } from '@nestjs/common';
2 | import { AppService } from './app.service';
3 | import { ApiOperation, ApiTags } from '@nestjs/swagger';
4 | // import { Auth } from './auth/decorators/auth-decorator';
5 | // import { AuthType } from './auth/enums/auth-type.enum';
6 |
7 | @ApiTags('App')
8 | @Controller()
9 | export class AppController {
10 | constructor(private readonly appService: AppService) {}
11 | // @Auth(AuthType.None) // this route is public
12 | @Get()
13 | @ApiOperation({summary: 'Get Hello World Message'})
14 | getHello(): string {
15 | return this.appService.getHello();
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/backend/src/puzzle-dependency/puzzle-dependency.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { Test, TestingModule } from '@nestjs/testing';
2 | import { PuzzleDependencyService } from './puzzle-dependency.service';
3 |
4 | describe('PuzzleDependencyService', () => {
5 | let service: PuzzleDependencyService;
6 |
7 | beforeEach(async () => {
8 | const module: TestingModule = await Test.createTestingModule({
9 | providers: [PuzzleDependencyService],
10 | }).compile();
11 |
12 | service = module.get(PuzzleDependencyService);
13 | });
14 |
15 | it('should be defined', () => {
16 | expect(service).toBeDefined();
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/backend/src/user-activity-log/entities/activity-log.entity.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Entity,
3 | PrimaryGeneratedColumn,
4 | Column,
5 | CreateDateColumn,
6 | Index,
7 | } from 'typeorm';
8 |
9 | @Entity('activity_logs')
10 | export class ActivityLog {
11 | @PrimaryGeneratedColumn('uuid')
12 | id: string;
13 |
14 | @Column()
15 | @Index()
16 | userId: string;
17 |
18 | @Column()
19 | @Index()
20 | actionType: string; // e.g., 'PUZZLE_ATTEMPT', 'QUIZ_SUBMISSION', 'REWARD_CLAIM'
21 |
22 | @Column('jsonb', { nullable: true })
23 | metadata: Record; // Flexible structure
24 |
25 | @CreateDateColumn()
26 | timestamp: Date;
27 | }
28 |
--------------------------------------------------------------------------------
/backend/src/user-ranking/entities/user-ranking.entity.ts:
--------------------------------------------------------------------------------
1 | import { Entity, PrimaryGeneratedColumn, Column, Index } from 'typeorm';
2 |
3 | @Entity()
4 | export class UserRank {
5 | @PrimaryGeneratedColumn('uuid')
6 | id: string;
7 |
8 | @Column()
9 | @Index({ unique: true })
10 | userId: string;
11 |
12 | @Column({ default: 0 })
13 | score: number;
14 |
15 | @Column({ default: 0 })
16 | achievements: number;
17 |
18 | @Column({ default: 0 })
19 | activityPoints: number;
20 |
21 | @Column({ default: 0 })
22 | rank: number;
23 |
24 | @Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' })
25 | lastUpdated: Date;
26 | }
27 |
--------------------------------------------------------------------------------
/backend/src/content-rating/content-rating.controller.spec.ts:
--------------------------------------------------------------------------------
1 | import { Test, TestingModule } from '@nestjs/testing';
2 | import { ContentRatingController } from './content-rating.controller';
3 |
4 | describe('ContentRatingController', () => {
5 | let controller: ContentRatingController;
6 |
7 | beforeEach(async () => {
8 | const module: TestingModule = await Test.createTestingModule({
9 | controllers: [ContentRatingController],
10 | }).compile();
11 |
12 | controller = module.get(ContentRatingController);
13 | });
14 |
15 | it('should be defined', () => {
16 | expect(controller).toBeDefined();
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/backend/src/progress/entities/progress.entity.ts:
--------------------------------------------------------------------------------
1 | import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from 'typeorm';
2 |
3 | @Entity('progress')
4 | export class Progress {
5 | @PrimaryGeneratedColumn('uuid')
6 | id: string;
7 |
8 | @Column({ type: 'uuid', unique: true })
9 | userId: string;
10 |
11 | @Column({ default: 0 })
12 | completedPuzzles: number;
13 |
14 | @Column({ default: 0 })
15 | totalPuzzles: number;
16 |
17 | @Column({ type: 'float', default: 0 })
18 | percentComplete: number;
19 |
20 | @UpdateDateColumn()
21 | lastUpdated: Date;
22 |
23 | @CreateDateColumn()
24 | createdAt: Date;
25 | }
26 |
27 |
--------------------------------------------------------------------------------
/backend/src/quiz/quiz.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { QuizService } from './services/quiz.service';
3 | import { TypeOrmModule } from '@nestjs/typeorm';
4 | import { Quiz } from './entities/quiz.entity';
5 | import { QuizOption } from './entities/quiz-option.entity';
6 | import { QuizQuestion } from './entities/quiz-question.entity';
7 | import { QuizController } from './controllers/quiz.controller';
8 |
9 | @Module({
10 | imports: [TypeOrmModule.forFeature([QuizOption, QuizQuestion, Quiz])],
11 | controllers: [QuizController],
12 | providers: [QuizService],
13 | exports: [QuizService, TypeOrmModule],
14 | })
15 | export class QuizModule {}
16 |
--------------------------------------------------------------------------------
/backend/src/feedback/feedback.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from "@nestjs/common"
2 | import { TypeOrmModule } from "@nestjs/typeorm"
3 | import { ConfigModule } from "@nestjs/config"
4 | import { FeedbackController } from "./controllers/feedback.controller"
5 | import { FeedbackService } from "./services/feedback.service"
6 | import { AdminGuard } from "./guards/admin.guard"
7 | import { Feedback } from "./entities/feedback.entity"
8 |
9 | @Module({
10 | imports: [ConfigModule, TypeOrmModule.forFeature([Feedback])],
11 | controllers: [FeedbackController],
12 | providers: [FeedbackService, AdminGuard],
13 | exports: [FeedbackService],
14 | })
15 | export class FeedbackModule {}
16 |
--------------------------------------------------------------------------------
/backend/src/quiz/quiz.controller.spec.ts:
--------------------------------------------------------------------------------
1 | import { Test, TestingModule } from '@nestjs/testing';
2 | import { QuizController } from './quiz.controller';
3 | import { QuizService } from './services/quiz.service';
4 |
5 | describe('QuizController', () => {
6 | let controller: QuizController;
7 |
8 | beforeEach(async () => {
9 | const module: TestingModule = await Test.createTestingModule({
10 | controllers: [QuizController],
11 | providers: [QuizService],
12 | }).compile();
13 |
14 | controller = module.get(QuizController);
15 | });
16 |
17 | it('should be defined', () => {
18 | expect(controller).toBeDefined();
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/backend/src/reports/entities/report.entity.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Entity,
3 | Column,
4 | PrimaryGeneratedColumn,
5 | CreateDateColumn,
6 | UpdateDateColumn,
7 | Unique,
8 | } from 'typeorm';
9 |
10 | @Entity()
11 | @Unique(['puzzleId', 'userId']) // Prevent duplicate reports
12 | export class Report {
13 | @PrimaryGeneratedColumn()
14 | id: number;
15 |
16 | @Column()
17 | puzzleId: number;
18 |
19 | @Column()
20 | userId: number;
21 |
22 | @Column()
23 | message: string;
24 |
25 | @Column({ default: false })
26 | resolved: boolean;
27 |
28 | @CreateDateColumn()
29 | createdAt: Date;
30 |
31 | @UpdateDateColumn()
32 | updatedAt: Date;
33 | }
34 |
--------------------------------------------------------------------------------
/backend/src/token-verification/index.ts:
--------------------------------------------------------------------------------
1 | // Main exports for the module
2 | export { TokenVerificationModule } from "./token-verification.module"
3 | export { VerificationService } from "./services/verification.service"
4 | export { JwtGuard, JwtOptions } from "./guards/jwt.guard"
5 | export { WalletGuard, WalletOptions } from "./guards/wallet.guard"
6 | export { TokenLoggingInterceptor } from "./interceptors/token-logging.interceptor"
7 | export { TokenHeaderInterceptor } from "./interceptors/token-header.interceptor"
8 | export { TokenPayload, WalletPayload } from "./decorators/token-payload.decorator"
9 |
10 | // Interface exports
11 | export * from "./interfaces/token.interface"
12 |
--------------------------------------------------------------------------------
/backend/src/content-rating/entities/content-rating.entity.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Entity,
3 | Column,
4 | PrimaryGeneratedColumn,
5 | CreateDateColumn,
6 | Unique,
7 | ManyToOne,
8 | Index,
9 | } from 'typeorm';
10 |
11 | @Entity('content_ratings')
12 | @Unique(['userId', 'contentId']) // Enforces one rating per user per content
13 | export class ContentRating {
14 | @PrimaryGeneratedColumn('uuid')
15 | id: string;
16 |
17 | @Column()
18 | @Index()
19 | userId: string;
20 |
21 | @Column()
22 | @Index()
23 | contentId: string;
24 |
25 | @Column('int')
26 | rating: number; // Typically from 1 to 5
27 |
28 | @CreateDateColumn()
29 | createdAt: Date;
30 | }
31 |
--------------------------------------------------------------------------------
/backend/src/user-activity-log/user-activity-log.controller.spec.ts:
--------------------------------------------------------------------------------
1 | import { Test, TestingModule } from '@nestjs/testing';
2 | import { UserActivityLogController } from './user-activity-log.controller';
3 |
4 | describe('UserActivityLogController', () => {
5 | let controller: UserActivityLogController;
6 |
7 | beforeEach(async () => {
8 | const module: TestingModule = await Test.createTestingModule({
9 | controllers: [UserActivityLogController],
10 | }).compile();
11 |
12 | controller = module.get(UserActivityLogController);
13 | });
14 |
15 | it('should be defined', () => {
16 | expect(controller).toBeDefined();
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: CONTRACT
2 |
3 | on:
4 | workflow_dispatch:
5 | push:
6 | branches:
7 | - main
8 | pull_request:
9 | permissions: read-all
10 |
11 | jobs:
12 | build:
13 | runs-on: ubuntu-latest
14 | steps:
15 | - uses: actions/checkout@v4
16 | - uses: asdf-vm/actions/install@v3
17 | - run: scarb fmt --check
18 | working-directory: onchain
19 | - run: scarb build
20 | working-directory: onchain
21 |
22 | test:
23 | runs-on: ubuntu-latest
24 | steps:
25 | - uses: actions/checkout@v4
26 | - uses: asdf-vm/actions/install@v3
27 | - run: snforge test
28 | working-directory: onchain
29 |
--------------------------------------------------------------------------------
/frontend/components/puzzles/roadmap/puzzleRoadmapData.ts:
--------------------------------------------------------------------------------
1 | export const puzzleRoadmap = [
2 | {
3 | id: '1',
4 | title: 'Genesis Puzzle',
5 | description: 'Start your journey into puzzles.',
6 | imageUrl: '/images/genesis.png',
7 | releaseDate: '2025-08-01',
8 | },
9 | {
10 | id: '2',
11 | title: 'Cipher Cave',
12 | description: 'Decrypt hidden secrets in this thrilling challenge.',
13 | imageUrl: '/images/cipher.png',
14 | releaseDate: '2025-08-15',
15 | },
16 | {
17 | id: '3',
18 | title: 'Maze of Mind',
19 | description: 'A psychological twist awaits.',
20 | imageUrl: '/images/maze.png',
21 | releaseDate: '2025-09-01',
22 | },
23 | ];
--------------------------------------------------------------------------------
/backend/src/api-key/api-key.controller.spec.ts:
--------------------------------------------------------------------------------
1 | import { Test, TestingModule } from '@nestjs/testing';
2 | import { ApiKeyController } from './api-key.controller';
3 | import { ApiKeyService } from './api-key.service';
4 |
5 | describe('ApiKeyController', () => {
6 | let controller: ApiKeyController;
7 |
8 | beforeEach(async () => {
9 | const module: TestingModule = await Test.createTestingModule({
10 | controllers: [ApiKeyController],
11 | providers: [ApiKeyService],
12 | }).compile();
13 |
14 | controller = module.get(ApiKeyController);
15 | });
16 |
17 | it('should be defined', () => {
18 | expect(controller).toBeDefined();
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/backend/src/puzzle-dependency/entities/puzzle-completion.entity.ts:
--------------------------------------------------------------------------------
1 | import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, Index } from 'typeorm';
2 |
3 | @Entity('puzzle_completions')
4 | @Index(['userId', 'puzzleId'], { unique: true })
5 | export class PuzzleCompletion {
6 | @PrimaryGeneratedColumn('uuid')
7 | id: string;
8 |
9 | @Column({ name: 'user_id' })
10 | @Index()
11 | userId: string;
12 |
13 | @Column({ name: 'puzzle_id' })
14 | @Index()
15 | puzzleId: string;
16 |
17 | @Column({ name: 'completed_at', type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' })
18 | completedAt: Date;
19 |
20 | @CreateDateColumn({ name: 'created_at' })
21 | createdAt: Date;
22 | }
23 |
--------------------------------------------------------------------------------
/backend/src/reports/reports.controller.spec.ts:
--------------------------------------------------------------------------------
1 | import { Test, TestingModule } from '@nestjs/testing';
2 | import { ReportsController } from './reports.controller';
3 | import { ReportsService } from './reports.service';
4 |
5 | describe('ReportsController', () => {
6 | let controller: ReportsController;
7 |
8 | beforeEach(async () => {
9 | const module: TestingModule = await Test.createTestingModule({
10 | controllers: [ReportsController],
11 | providers: [ReportsService],
12 | }).compile();
13 |
14 | controller = module.get(ReportsController);
15 | });
16 |
17 | it('should be defined', () => {
18 | expect(controller).toBeDefined();
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/backend/src/activity/entities/activity.entity.ts:
--------------------------------------------------------------------------------
1 | import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, ManyToOne } from 'typeorm';
2 |
3 | export enum ActivityType {
4 | LOGIN = 'LOGIN',
5 | PUZZLE_ATTEMPT = 'PUZZLE_ATTEMPT',
6 | NFT_MINT = 'NFT_MINT',
7 | }
8 |
9 | @Entity()
10 | export class Activity {
11 | @PrimaryGeneratedColumn('uuid')
12 | id: string;
13 |
14 | @ManyToOne(() => User, (user) => user.activities, { onDelete: 'CASCADE' })
15 | user: User;
16 |
17 | @Column({ type: 'enum', enum: ActivityType })
18 | type: ActivityType;
19 |
20 | @Column({ type: 'jsonb', nullable: true })
21 | metadata: Record;
22 |
23 | @CreateDateColumn()
24 | createdAt: Date;
25 | }
--------------------------------------------------------------------------------
/backend/src/rate-limiter/rate-limiter.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@nestjs/common';
2 |
3 | @Injectable()
4 | export class RateLimiterService {
5 | private requestsMap = new Map();
6 |
7 | isRateLimited(key: string, ttl: number, limit: number): boolean {
8 | const now = Date.now();
9 | const entry = this.requestsMap.get(key);
10 |
11 | if (!entry || now > entry.expiresAt) {
12 | this.requestsMap.set(key, { count: 1, expiresAt: now + ttl * 1000 });
13 | return false;
14 | }
15 |
16 | if (entry.count >= limit) return true;
17 |
18 | entry.count += 1;
19 | this.requestsMap.set(key, entry);
20 | return false;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/frontend/components/AnimatedBlurBackground.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const AnimatedBlurBackground = () => {
4 | return (
5 |
10 | );
11 | };
12 |
13 | export default AnimatedBlurBackground;
14 |
--------------------------------------------------------------------------------
/backend/src/achievements/achievements.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { TypeOrmModule } from '@nestjs/typeorm';
3 | import { Achievement } from './entities/achievement.entity';
4 | import { PlayerAchievement } from './entities/player-achievements.entity';
5 | import { AchievementController } from './achievements.controller';
6 | import { AchievementService } from './achievements.service';
7 |
8 | @Module({
9 | imports: [TypeOrmModule.forFeature([Achievement, PlayerAchievement])],
10 | providers: [AchievementService],
11 | controllers: [AchievementController],
12 | exports: [AchievementService], // Export service for potential use by other modules
13 | })
14 | export class AchievementModule {}
15 |
--------------------------------------------------------------------------------
/backend/src/analytics/analytics.controller.spec.ts:
--------------------------------------------------------------------------------
1 | import { Test, TestingModule } from '@nestjs/testing';
2 | import { AnalyticsController } from './analytics.controller';
3 | import { AnalyticsService } from './analytics.service';
4 |
5 | describe('AnalyticsController', () => {
6 | let controller: AnalyticsController;
7 |
8 | beforeEach(async () => {
9 | const module: TestingModule = await Test.createTestingModule({
10 | controllers: [AnalyticsController],
11 | providers: [AnalyticsService],
12 | }).compile();
13 |
14 | controller = module.get(AnalyticsController);
15 | });
16 |
17 | it('should be defined', () => {
18 | expect(controller).toBeDefined();
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/backend/src/promo-code/promo-code.controller.ts:
--------------------------------------------------------------------------------
1 | // src/promo-code/promo-code.controller.ts
2 | import { Controller, Post, Body, UseGuards, Request } from '@nestjs/common';
3 | import { RedeemPromoCodeDto } from 'src/promo-code/dto/redeem-promo-code.dto';
4 | import { PromoCodeService } from './promo-code.service';
5 | import { JwtAuthGuard } from 'src/auth/guards/jwt-auth.guard';
6 |
7 | @Controller('promocode')
8 | export class PromoCodeController {
9 | constructor(private readonly promoCodeService: PromoCodeService) {}
10 |
11 | @UseGuards(JwtAuthGuard)
12 | @Post('redeem')
13 | redeem(@Body() dto: RedeemPromoCodeDto, @Request() req) {
14 | return this.promoCodeService.redeem(dto.code, req.user.userId);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/backend/src/time-trial/time-trial.entity.ts:
--------------------------------------------------------------------------------
1 | import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, Index } from "typeorm"
2 |
3 | @Entity("time_trials")
4 | export class TimeTrial {
5 | @PrimaryGeneratedColumn("uuid")
6 | id: string
7 |
8 | @Column("uuid")
9 | @Index()
10 | userId: string
11 |
12 | @Column("uuid")
13 | @Index()
14 | puzzleId: string
15 |
16 | @Column({ type: "timestamp" })
17 | startTime: Date
18 |
19 | @Column({ type: "timestamp", nullable: true })
20 | endTime: Date
21 |
22 | @Column({ default: false })
23 | completed: boolean
24 |
25 | @CreateDateColumn()
26 | createdAt: Date
27 |
28 | @UpdateDateColumn()
29 | updatedAt: Date
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/backend/src/app.controller.spec.ts:
--------------------------------------------------------------------------------
1 | import { Test, TestingModule } from '@nestjs/testing';
2 | import { AppController } from './app.controller';
3 | import { AppService } from './app.service';
4 |
5 | describe('AppController', () => {
6 | let appController: AppController;
7 |
8 | beforeEach(async () => {
9 | const app: TestingModule = await Test.createTestingModule({
10 | controllers: [AppController],
11 | providers: [AppService],
12 | }).compile();
13 |
14 | appController = app.get(AppController);
15 | });
16 |
17 | describe('root', () => {
18 | it('should return "Hello World!"', () => {
19 | expect(appController.getHello()).toBe('Hello World!');
20 | });
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/backend/src/content-rating/content-rating.controller.ts:
--------------------------------------------------------------------------------
1 | import { Body, Controller, Get, Post, Query } from '@nestjs/common';
2 | import { ContentRatingService } from './content-rating.service';
3 | import { CreateRatingDto } from './dto/create-rating.dto';
4 |
5 | @Controller('ratings')
6 | export class ContentRatingController {
7 | constructor(private readonly ratingService: ContentRatingService) {}
8 |
9 | @Post()
10 | async rateContent(@Body() dto: CreateRatingDto) {
11 | return this.ratingService.rateContent(dto.userId, dto.contentId, dto.rating);
12 | }
13 |
14 | @Get('/stats')
15 | async getStats(@Query('contentId') contentId: string) {
16 | return this.ratingService.getContentRatingStats(contentId);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/backend/src/promo-code/entities/promo-code-redemption.entity.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Entity,
3 | PrimaryGeneratedColumn,
4 | ManyToOne,
5 | CreateDateColumn,
6 | Unique,
7 | } from 'typeorm';
8 | import { PromoCode } from 'src/promo-code/entities/promo-code.entities'
9 | import { User } from 'src/auth/entities/user.entity';
10 |
11 | @Entity('promo_code_redemptions')
12 | @Unique(['promoCode', 'user']) // ensures one-time redemption per user
13 | export class PromoCodeRedemption {
14 | @PrimaryGeneratedColumn('uuid')
15 | id: string;
16 |
17 | @ManyToOne(() => PromoCode, { eager: true })
18 | promoCode: PromoCode;
19 |
20 | @ManyToOne(() => User)
21 | user: User;
22 |
23 | @CreateDateColumn()
24 | redeemedAt: Date;
25 | }
26 |
--------------------------------------------------------------------------------
/backend/src/user-activity-log/user-activity-log.controller.ts:
--------------------------------------------------------------------------------
1 | import { Controller, Get, Query } from '@nestjs/common';
2 | import { UserActivityLogService } from './user-activity-log.service';
3 | import { FilterActivityDto } from './dto/filter-activity.dto';
4 |
5 | @Controller('admin/activity-logs')
6 | export class UserActivityLogController {
7 | constructor(private readonly logService: UserActivityLogService) {}
8 |
9 | @Get()
10 | async getFilteredLogs(@Query() query: FilterActivityDto) {
11 | return this.logService.filterLogs({
12 | ...query,
13 | startDate: query.startDate ? new Date(query.startDate) : undefined,
14 | endDate: query.endDate ? new Date(query.endDate) : undefined,
15 | });
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/backend/src/puzzle-category/puzzle-category.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from "@nestjs/common"
2 | import { TypeOrmModule } from "@nestjs/typeorm"
3 | import { PuzzleCategoryController } from "./puzzle-category.controller"
4 | import { PuzzleCategoryService } from "./puzzle-category.service"
5 | import { Category } from "./entities/category.entity"
6 | import { CategoryPuzzle } from "./entities/puzzle.entity" // Updated import
7 |
8 | @Module({
9 | imports: [
10 | TypeOrmModule.forFeature([Category, CategoryPuzzle]), // Updated entity name
11 | ],
12 | controllers: [PuzzleCategoryController],
13 | providers: [PuzzleCategoryService],
14 | exports: [PuzzleCategoryService],
15 | })
16 | export class PuzzleCategoryModule {}
17 |
--------------------------------------------------------------------------------
/backend/src/reward-shop/reward-shop.controller.spec.ts:
--------------------------------------------------------------------------------
1 | import { Test, TestingModule } from '@nestjs/testing';
2 | import { RewardShopController } from './reward-shop.controller';
3 | import { RewardShopService } from './reward-shop.service';
4 |
5 | describe('RewardShopController', () => {
6 | let controller: RewardShopController;
7 |
8 | beforeEach(async () => {
9 | const module: TestingModule = await Test.createTestingModule({
10 | controllers: [RewardShopController],
11 | providers: [RewardShopService],
12 | }).compile();
13 |
14 | controller = module.get(RewardShopController);
15 | });
16 |
17 | it('should be defined', () => {
18 | expect(controller).toBeDefined();
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/backend/src/common/gaurds/roles.gaurds.ts:
--------------------------------------------------------------------------------
1 | import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
2 | import { Reflector } from '@nestjs/core';
3 | import { ROLES_KEY } from '../decorators/roles.decorator';
4 |
5 | @Injectable()
6 | export class RolesGuard implements CanActivate {
7 | constructor(private reflector: Reflector) {}
8 |
9 | canActivate(context: ExecutionContext): boolean {
10 | const requiredRoles = this.reflector.getAllAndOverride(
11 | ROLES_KEY,
12 | [context.getHandler(), context.getClass()],
13 | );
14 |
15 | if (!requiredRoles) return true;
16 |
17 | const { user } = context.switchToHttp().getRequest();
18 | return requiredRoles.includes(user?.role);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/backend/src/in-app-notifications/dto/system-notification.dto.ts:
--------------------------------------------------------------------------------
1 | import { IsString, IsEnum, IsNotEmpty } from 'class-validator';
2 | import { ApiProperty } from '@nestjs/swagger';
3 | import { InAppNotificationType } from '../entities/in-app-notification.entity';
4 |
5 | export class SystemNotificationDto {
6 | @ApiProperty({ description: 'Title of the system notification' })
7 | @IsString()
8 | @IsNotEmpty()
9 | title: string;
10 |
11 | @ApiProperty({ description: 'Message content of the system notification' })
12 | @IsString()
13 | @IsNotEmpty()
14 | message: string;
15 |
16 | @ApiProperty({ description: 'Type of notification', enum: InAppNotificationType })
17 | @IsEnum(InAppNotificationType)
18 | type: InAppNotificationType;
19 | }
--------------------------------------------------------------------------------
/backend/src/user-inventory/user-inventory.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { TypeOrmModule } from '@nestjs/typeorm';
3 | import { UserInventoryController } from './user-inventory.controller';
4 | import { UserInventoryService } from './user-inventory.service';
5 | import { Inventory } from './entities/inventory';
6 | import { User } from './entities/user';
7 | import { NFT } from './entities/nft';
8 | import { Badge } from './entities/badge';
9 |
10 | @Module({
11 | imports: [
12 | TypeOrmModule.forFeature([Inventory, User, NFT, Badge]),
13 | ],
14 | controllers: [UserInventoryController],
15 | providers: [UserInventoryService],
16 | exports: [UserInventoryService],
17 | })
18 | export class UserInventoryModule {}
--------------------------------------------------------------------------------
/backend/src/user/entities/user.entity.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Entity,
3 | Column,
4 | PrimaryGeneratedColumn,
5 | CreateDateColumn,
6 | UpdateDateColumn,
7 | } from 'typeorm';
8 |
9 | @Entity('users')
10 | export class User {
11 | @PrimaryGeneratedColumn('uuid')
12 | id: string;
13 |
14 | @Column({ unique: true, length: 30 })
15 | username: string;
16 |
17 | @Column({ unique: true })
18 | email: string;
19 |
20 | @Column({ nullable: true })
21 | walletAddress?: string;
22 |
23 | @Column({ type: 'text', nullable: true })
24 | bio?: string;
25 |
26 | @Column({ nullable: true })
27 | avatarUrl?: string;
28 |
29 | @CreateDateColumn()
30 | createdAt: Date;
31 |
32 | @UpdateDateColumn()
33 | updatedAt: Date;
34 | }
35 |
--------------------------------------------------------------------------------
/backend/src/admin/strategies/local.strategy.ts:
--------------------------------------------------------------------------------
1 | import { Strategy } from 'passport-local';
2 | import { PassportStrategy } from '@nestjs/passport';
3 | import { Injectable, UnauthorizedException } from '@nestjs/common';
4 | import { AdminService } from '../admin.service';
5 |
6 | @Injectable()
7 | export class LocalStrategy extends PassportStrategy(Strategy, 'admin-local') {
8 | constructor(private adminService: AdminService) {
9 | super({ usernameField: 'email' });
10 | }
11 |
12 | async validate(email: string, password: string) {
13 | const admin = await this.adminService.validateAdmin(email, password);
14 | if (!admin) {
15 | throw new UnauthorizedException('Invalid credentials');
16 | }
17 | return admin;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/backend/test/app.e2e-spec.ts:
--------------------------------------------------------------------------------
1 | import { Test, TestingModule } from '@nestjs/testing';
2 | import { INestApplication } from '@nestjs/common';
3 | import * as request from 'supertest';
4 | import { AppModule } from './../src/app.module';
5 |
6 | describe('AppController (e2e)', () => {
7 | let app: INestApplication;
8 |
9 | beforeEach(async () => {
10 | const moduleFixture: TestingModule = await Test.createTestingModule({
11 | imports: [AppModule],
12 | }).compile();
13 |
14 | app = moduleFixture.createNestApplication();
15 | await app.init();
16 | });
17 |
18 | it('/ (GET)', () => {
19 | return request(app.getHttpServer())
20 | .get('/')
21 | .expect(200)
22 | .expect('Hello World!');
23 | });
24 | });
25 |
--------------------------------------------------------------------------------
/backend/src/achievements/achievements.controller.spec.ts:
--------------------------------------------------------------------------------
1 | import { Test, TestingModule } from '@nestjs/testing';
2 | import { AchievementController } from './achievements.controller';
3 | import { AchievementService } from './achievements.service';
4 |
5 | describe('AchievementsController', () => {
6 | let controller: AchievementController;
7 |
8 | beforeEach(async () => {
9 | const module: TestingModule = await Test.createTestingModule({
10 | controllers: [AchievementController],
11 | providers: [AchievementService],
12 | }).compile();
13 |
14 | controller = module.get(AchievementController);
15 | });
16 |
17 | it('should be defined', () => {
18 | expect(controller).toBeDefined();
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/backend/src/puzzle-dependency/puzzle-dependency.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { TypeOrmModule } from '@nestjs/typeorm';
3 | import { PuzzleDependency } from './entities/puzzle-dependency.entity';
4 | import { PuzzleCompletion } from './entities/puzzle-completion.entity';
5 | import { PuzzleDependencyService } from './puzzle-dependency.service';
6 | import { PuzzleDependencyController } from './puzzle-dependency.controller';
7 |
8 | @Module({
9 | imports: [
10 | TypeOrmModule.forFeature([PuzzleDependency, PuzzleCompletion])
11 | ],
12 | controllers: [PuzzleDependencyController],
13 | providers: [PuzzleDependencyService],
14 | exports: [PuzzleDependencyService],
15 | })
16 | export class PuzzleDependencyModule {}
17 |
18 |
--------------------------------------------------------------------------------
/backend/src/user-inventory/entities/badge.ts:
--------------------------------------------------------------------------------
1 | import { Entity, PrimaryGeneratedColumn, Column, OneToMany, CreateDateColumn } from 'typeorm';
2 | import { Inventory } from './inventory';
3 |
4 | @Entity('badges')
5 | export class Badge {
6 | @PrimaryGeneratedColumn('uuid')
7 | id: string;
8 |
9 | @Column()
10 | name: string;
11 |
12 | @Column()
13 | description: string;
14 |
15 | @Column()
16 | achievementType: string; // puzzle_solver, speed_runner, explorer, etc.
17 |
18 | @Column()
19 | iconUrl: string;
20 |
21 | @Column({ default: true })
22 | isActive: boolean;
23 |
24 | // @OneToMany(() => Inventory, inventory => inventory.badge)
25 | // inventoryItems: Inventory[];
26 |
27 | @CreateDateColumn()
28 | createdAt: Date;
29 | }
--------------------------------------------------------------------------------
/backend/src/user-ranking/user-ranking.controller.spec.ts:
--------------------------------------------------------------------------------
1 | import { Test, TestingModule } from '@nestjs/testing';
2 | import { UserRankingController } from './user-ranking.controller';
3 | import { UserRankingService } from './user-ranking.service';
4 |
5 | describe('UserRankingController', () => {
6 | let controller: UserRankingController;
7 |
8 | beforeEach(async () => {
9 | const module: TestingModule = await Test.createTestingModule({
10 | controllers: [UserRankingController],
11 | providers: [UserRankingService],
12 | }).compile();
13 |
14 | controller = module.get(UserRankingController);
15 | });
16 |
17 | it('should be defined', () => {
18 | expect(controller).toBeDefined();
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/frontend/components/ui/textarea.jsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 |
3 | import { cn } from "@/lib/utils"
4 |
5 | const Textarea = React.forwardRef(({ className, ...props }, ref) => {
6 | return (
7 | ()
14 | );
15 | })
16 | Textarea.displayName = "Textarea"
17 |
18 | export { Textarea }
19 |
--------------------------------------------------------------------------------
/backend/src/achievements/entities/player-achievements.entity.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Entity,
3 | PrimaryGeneratedColumn,
4 | Column,
5 | CreateDateColumn,
6 | ManyToOne,
7 | JoinColumn,
8 | } from 'typeorm';
9 | import { Achievement } from './achievement.entity';
10 |
11 | @Entity('player_achievements')
12 | export class PlayerAchievement {
13 | @PrimaryGeneratedColumn('uuid')
14 | id: string;
15 |
16 | @Column({ type: 'uuid', name: 'player_id' })
17 | playerId: string;
18 |
19 | @Column({ type: 'uuid', name: 'achievement_id' })
20 | achievementId: string;
21 |
22 | @CreateDateColumn({ name: 'earned_at' })
23 | earnedAt: Date;
24 |
25 | @ManyToOne(() => Achievement)
26 | @JoinColumn({ name: 'achievement_id' })
27 | achievement: Achievement;
28 | }
29 |
--------------------------------------------------------------------------------
/backend/src/reports/reports.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@nestjs/common';
2 | import { CreateReportDto } from './dto/create-report.dto';
3 | import { UpdateReportDto } from './dto/update-report.dto';
4 |
5 | @Injectable()
6 | export class ReportsService {
7 | create(createReportDto: CreateReportDto) {
8 | return 'This action adds a new report';
9 | }
10 |
11 | findAll() {
12 | return `This action returns all reports`;
13 | }
14 |
15 | findOne(id: number) {
16 | return `This action returns a #${id} report`;
17 | }
18 |
19 | update(id: number, updateReportDto: UpdateReportDto) {
20 | return `This action updates a #${id} report`;
21 | }
22 |
23 | remove(id: number) {
24 | return `This action removes a #${id} report`;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/frontend/components/puzzles/roadmap/PuzzleTimeline.tsx:
--------------------------------------------------------------------------------
1 | import PuzzleCard from './PuzzleCard';
2 |
3 | export type Puzzle = {
4 | id: string;
5 | title: string;
6 | description: string;
7 | imageUrl: string;
8 | releaseDate: string;
9 | };
10 |
11 | type Props = {
12 | puzzles: Puzzle[];
13 | };
14 |
15 | export default function PuzzleTimeline({ puzzles }: Props) {
16 | return (
17 |
18 |
19 | {puzzles.map((puzzle) => (
20 |
23 | ))}
24 |
25 |
26 | );
27 | }
28 |
--------------------------------------------------------------------------------
/backend/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | parser: '@typescript-eslint/parser',
3 | parserOptions: {
4 | project: 'tsconfig.json',
5 | tsconfigRootDir: __dirname,
6 | sourceType: 'module',
7 | },
8 | plugins: ['@typescript-eslint/eslint-plugin'],
9 | extends: [
10 | 'plugin:@typescript-eslint/recommended',
11 | // 'plugin:prettier/recommended',
12 | ],
13 | root: true,
14 | env: {
15 | node: true,
16 | jest: true,
17 | },
18 | ignorePatterns: ['.eslintrc.js'],
19 | rules: {
20 | '@typescript-eslint/interface-name-prefix': 'off',
21 | '@typescript-eslint/explicit-function-return-type': 'off',
22 | '@typescript-eslint/explicit-module-boundary-types': 'off',
23 | '@typescript-eslint/no-explicit-any': 'off',
24 | },
25 | };
26 |
--------------------------------------------------------------------------------
/backend/src/puzzle-comment/puzzle-comment.controller.spec.ts:
--------------------------------------------------------------------------------
1 | import { Test, TestingModule } from '@nestjs/testing';
2 | import { PuzzleCommentController } from './puzzle-comment.controller';
3 | import { PuzzleCommentService } from './puzzle-comment.service';
4 |
5 | describe('PuzzleCommentController', () => {
6 | let controller: PuzzleCommentController;
7 |
8 | beforeEach(async () => {
9 | const module: TestingModule = await Test.createTestingModule({
10 | controllers: [PuzzleCommentController],
11 | providers: [PuzzleCommentService],
12 | }).compile();
13 |
14 | controller = module.get(PuzzleCommentController);
15 | });
16 |
17 | it('should be defined', () => {
18 | expect(controller).toBeDefined();
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/backend/src/puzzle-fork/puzzle-fork.controller.ts:
--------------------------------------------------------------------------------
1 | import { Controller, Post, Body, UsePipes, ValidationPipe } from '@nestjs/common';
2 | import { PuzzleForkService } from './puzzle-fork.service';
3 | import { CreateForkDto } from './dto/create-fork.dto';
4 |
5 | @Controller('puzzles')
6 | export class PuzzleForkController {
7 | constructor(private readonly puzzleForkService: PuzzleForkService) {}
8 |
9 | /**
10 | * Admin-only endpoint to fork a puzzle.
11 | * In a real application, this should be protected with an AdminGuard.
12 | * Ex: @UseGuards(AdminGuard)
13 | */
14 | @Post('fork')
15 | @UsePipes(new ValidationPipe({ transform: true }))
16 | forkPuzzle(@Body() createForkDto: CreateForkDto) {
17 | return this.puzzleForkService.fork(createForkDto);
18 | }
19 | }
--------------------------------------------------------------------------------
/backend/src/audit-log/audit-log.controller.ts:
--------------------------------------------------------------------------------
1 | import { Controller, Get, Query } from '@nestjs/common';
2 | import { ApiTags, ApiQuery } from '@nestjs/swagger';
3 | import { AuditLogService } from './audit-log.service';
4 | import { FilterAuditLogDto } from './Dto/filter-audit-log.dto';
5 |
6 | @ApiTags('Audit Logs')
7 | @Controller('admin/audit-logs')
8 | export class AuditLogController {
9 | constructor(private readonly auditLogService: AuditLogService) {}
10 |
11 | @Get()
12 | async getAuditLogs(@Query() filter: FilterAuditLogDto) {
13 | return this.auditLogService.findAll({
14 | ...filter,
15 | startDate: filter.startDate ? new Date(filter.startDate) : undefined,
16 | endDate: filter.endDate ? new Date(filter.endDate) : undefined,
17 | });
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/backend/src/promo-code/entities/promo-code.module.ts:
--------------------------------------------------------------------------------
1 | // src/promo-code/promo-code.module.ts
2 | import { Module } from '@nestjs/common';
3 | import { TypeOrmModule } from '@nestjs/typeorm';
4 | import { PromoCode } from 'src/promo-code/entities/promo-code.entities';
5 | import { PromoCodeRedemption } from 'src/promo-code/entities/promo-code-redemption.entity';
6 | import { User } from 'src/auth/entities/user.entity';
7 | import { PromoCodeService } from 'src/promo-code/promo-code.service'
8 | import { PromoCodeController } from 'src/promo-code/promo-code.controller';
9 |
10 | @Module({
11 | imports: [TypeOrmModule.forFeature([PromoCode, PromoCodeRedemption, User])],
12 | controllers: [PromoCodeController],
13 | providers: [PromoCodeService],
14 | })
15 | export class PromoCodeModule {}
16 |
--------------------------------------------------------------------------------
/backend/src/in-app-notifications/in-app-notifications.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { TypeOrmModule } from '@nestjs/typeorm';
3 | import { InAppNotificationsController } from './in-app-notifications.controller';
4 | import { InAppNotificationsService } from './in-app-notifications.service';
5 | import { BroadcasterService } from './services/broadcaster.service';
6 | import { InAppNotification } from './entities/in-app-notification.entity';
7 |
8 | @Module({
9 | imports: [
10 | TypeOrmModule.forFeature([InAppNotification]),
11 | ],
12 | controllers: [InAppNotificationsController],
13 | providers: [InAppNotificationsService, BroadcasterService],
14 | exports: [InAppNotificationsService, BroadcasterService],
15 | })
16 | export class InAppNotificationsModule {}
--------------------------------------------------------------------------------
/backend/src/user-report-card/user-report-card.controller.spec.ts:
--------------------------------------------------------------------------------
1 | import { Test, TestingModule } from '@nestjs/testing';
2 | import { UserReportCardController } from './user-report-card.controller';
3 | import { UserReportCardService } from './user-report-card.service';
4 |
5 | describe('UserReportCardController', () => {
6 | let controller: UserReportCardController;
7 |
8 | beforeEach(async () => {
9 | const module: TestingModule = await Test.createTestingModule({
10 | controllers: [UserReportCardController],
11 | providers: [UserReportCardService],
12 | }).compile();
13 |
14 | controller = module.get(UserReportCardController);
15 | });
16 |
17 | it('should be defined', () => {
18 | expect(controller).toBeDefined();
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/backend/src/puzzle-fork/entities/forked-puzzle.entity.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Entity,
3 | PrimaryGeneratedColumn,
4 | Column,
5 | CreateDateColumn,
6 | Index,
7 | } from 'typeorm';
8 |
9 | @Entity('forked_puzzles')
10 | export class ForkedPuzzle {
11 | @PrimaryGeneratedColumn('uuid')
12 | id: string;
13 |
14 | // The ID of the original puzzle this was forked from
15 | @Index()
16 | @Column()
17 | originalPuzzleId: string;
18 |
19 | // The version number of the original puzzle at the time of forking
20 | @Column()
21 | forkedFromVersion: number;
22 |
23 | @Column()
24 | title: string;
25 |
26 | // Store the full content of the puzzle at the time of the fork
27 | @Column({ type: 'jsonb' })
28 | content: Record;
29 |
30 | @CreateDateColumn()
31 | forkedAt: Date;
32 | }
--------------------------------------------------------------------------------
/backend/src/puzzle/puzzle.entity.ts:
--------------------------------------------------------------------------------
1 | import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from "typeorm"
2 |
3 | @Entity("puzzles")
4 | export class Puzzle {
5 | @PrimaryGeneratedColumn("uuid") // Changed from auto-increment to UUID
6 | id: string
7 |
8 | @Column({ length: 255 })
9 | title: string
10 |
11 | @Column("text")
12 | description: string
13 |
14 | @Column({ length: 50 })
15 | difficulty: string
16 |
17 | @Column({ nullable: true })
18 | hint?: string
19 |
20 | @Column("text")
21 | solution: string
22 |
23 | @Column({ nullable: true })
24 | rewardId?: string
25 |
26 | @Column({ default: true })
27 | isActive: boolean
28 |
29 | @CreateDateColumn()
30 | createdAt: Date
31 |
32 | @UpdateDateColumn()
33 | updatedAt: Date
34 | }
35 |
--------------------------------------------------------------------------------
/backend/src/token-verification/interfaces/token.interface.ts:
--------------------------------------------------------------------------------
1 | export interface JwtPayload {
2 | sub: string
3 | iat?: number
4 | exp?: number
5 | [key: string]: any
6 | }
7 |
8 | export interface WalletTokenPayload {
9 | address: string
10 | signature: string
11 | message: string
12 | timestamp: number
13 | [key: string]: any
14 | }
15 |
16 | export interface TokenValidationResult {
17 | isValid: boolean
18 | payload?: JwtPayload | WalletTokenPayload
19 | error?: string
20 | expiresAt?: Date
21 | }
22 |
23 | export interface WalletVerificationOptions {
24 | maxAge?: number // Maximum age in milliseconds
25 | requiredMessage?: string
26 | }
27 |
28 | export interface JwtVerificationOptions {
29 | ignoreExpiration?: boolean
30 | audience?: string
31 | issuer?: string
32 | }
33 |
--------------------------------------------------------------------------------
/frontend/components/Rating.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Star } from "lucide-react";
3 |
4 | const Rating = ({
5 | totalStars = 5,
6 | filledStars,
7 |
8 | customstyles = "",
9 | }) => {
10 | const getStarIcon = (isFilled) => (
11 |
16 | );
17 |
18 | return (
19 |
20 | {Array.from({ length: totalStars }, (__, index) => (
21 |
22 | {getStarIcon(index < filledStars)}
23 |
24 | ))}
25 |
26 | );
27 | };
28 |
29 | export default Rating;
30 |
--------------------------------------------------------------------------------
/backend/src/puzzle-dependency/puzzle-dependency.controller.spec.ts:
--------------------------------------------------------------------------------
1 | import { Test, TestingModule } from '@nestjs/testing';
2 | import { PuzzleDependencyController } from './puzzle-dependency.controller';
3 | import { PuzzleDependencyService } from './puzzle-dependency.service';
4 |
5 | describe('PuzzleDependencyController', () => {
6 | let controller: PuzzleDependencyController;
7 |
8 | beforeEach(async () => {
9 | const module: TestingModule = await Test.createTestingModule({
10 | controllers: [PuzzleDependencyController],
11 | providers: [PuzzleDependencyService],
12 | }).compile();
13 |
14 | controller = module.get(PuzzleDependencyController);
15 | });
16 |
17 | it('should be defined', () => {
18 | expect(controller).toBeDefined();
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/backend/src/streak/dto/streak-stats.dto.ts:
--------------------------------------------------------------------------------
1 | import type { ActivityType } from "./activity-type.enum" // Assuming ActivityType is an enum or type defined elsewhere
2 |
3 | export class StreakStatsDto {
4 | userId: string
5 | currentStreak: number
6 | longestStreak: number
7 | totalActiveDays: number
8 | lastActivityDate: Date | null
9 | streakStartDate: Date | null
10 | isActive: boolean
11 | daysUntilReset: number
12 | streakPercentile?: number // Optional ranking
13 | }
14 |
15 | export class StreakLeaderboardDto {
16 | userId: string
17 | currentStreak: number
18 | longestStreak: number
19 | rank: number
20 | isActive: boolean
21 | }
22 |
23 | export class StreakHistoryDto {
24 | date: Date
25 | activityTypes: ActivityType[]
26 | activityCount: number
27 | streakDay: number
28 | }
29 |
--------------------------------------------------------------------------------
/backend/src/user-inventory/entities/nft.ts:
--------------------------------------------------------------------------------
1 | import { Entity, PrimaryGeneratedColumn, Column, OneToMany, CreateDateColumn } from 'typeorm';
2 | import { Inventory } from './inventory';
3 |
4 | @Entity('nfts')
5 | export class NFT {
6 | @PrimaryGeneratedColumn('uuid')
7 | id: string;
8 |
9 | @Column()
10 | name: string;
11 |
12 | @Column()
13 | description: string;
14 |
15 | @Column()
16 | imageUrl: string;
17 |
18 | @Column()
19 | rarity: string; // Common, Rare, Epic, Legendary
20 |
21 | @Column('jsonb', { nullable: true })
22 | metadata: Record;
23 |
24 | @Column({ default: true })
25 | isActive: boolean;
26 |
27 | // @OneToMany(() => Inventory, inventory => inventory.nft)
28 | // inventoryItems: Inventory[];
29 |
30 | @CreateDateColumn()
31 | createdAt: Date;
32 | }
--------------------------------------------------------------------------------
/backend/src/quiz/interfaces/quiz.interface.ts:
--------------------------------------------------------------------------------
1 | export interface QuizSubmission {
2 | questionId: string;
3 | selectedOptionIds: string[];
4 | }
5 |
6 | export interface QuizResult {
7 | quizId: string;
8 | totalQuestions: number;
9 | totalPoints: number;
10 | earnedPoints: number;
11 | percentage: number;
12 | passed: boolean;
13 | timeTaken?: number;
14 | answers: QuestionResult[];
15 | }
16 |
17 | export interface QuestionResult {
18 | questionId: string;
19 | question: string;
20 | selectedOptions: string[];
21 | correctOptions: string[];
22 | isCorrect: boolean;
23 | points: number;
24 | earnedPoints: number;
25 | explanation?: string;
26 | }
27 |
28 | export interface QuizFilters {
29 | topic?: string;
30 | isActive?: boolean;
31 | randomize?: boolean;
32 | limit?: number;
33 | }
34 |
--------------------------------------------------------------------------------
/frontend/app/page.tsx:
--------------------------------------------------------------------------------
1 | // app/link-accounts/page.tsx
2 | "use client";
3 |
4 | import React from "react";
5 | import { signIn, useSession } from "next-auth/react";
6 |
7 | export default function LinkAccounts() {
8 | const { data: session } = useSession();
9 |
10 | return (
11 |
12 |
Link your accounts
13 |
14 |
17 |
20 |
23 |
24 |
25 | );
26 | }
27 |
--------------------------------------------------------------------------------
/frontend/components/AchievementCard.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { CardContent, Card } from "./ui/card";
3 | const AchievementCard = ({ icon: Icon, value, label, color }) => {
4 | const colorClasses = {
5 | yellow: "text-yellow-400",
6 | purple: "text-purple-400",
7 | green: "text-green-400",
8 | };
9 | return (
10 |
11 |
12 |
13 |
14 |
{value}
15 |
{label}
16 |
17 |
18 |
19 | );
20 | };
21 |
22 | export default AchievementCard;
23 |
--------------------------------------------------------------------------------
/backend/src/multiplayer-queue/multiplayer-queue.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from "@nestjs/common"
2 | import { TypeOrmModule } from "@nestjs/typeorm"
3 | import { ScheduleModule } from "@nestjs/schedule"
4 | import { MultiplayerQueueService } from "./multiplayer-queue.service"
5 | import { MultiplayerQueueController } from "./multiplayer-queue.controller"
6 | import { Queue } from "./entities/queue.entity"
7 | import { Match } from "./entities/match.entity"
8 |
9 | @Module({
10 | imports: [
11 | TypeOrmModule.forFeature([Queue, Match]),
12 | ScheduleModule.forRoot(), // Enable cron jobs
13 | ],
14 | controllers: [MultiplayerQueueController],
15 | providers: [MultiplayerQueueService],
16 | exports: [MultiplayerQueueService], // Export for potential use in other modules
17 | })
18 | export class MultiplayerQueueModule {}
19 |
--------------------------------------------------------------------------------
/backend/src/in-app-notifications/dto/notification-response.dto.ts:
--------------------------------------------------------------------------------
1 | import { ApiProperty } from '@nestjs/swagger';
2 | import { InAppNotificationType } from '../entities/in-app-notification.entity';
3 |
4 | export class NotificationResponseDto {
5 | @ApiProperty()
6 | id: number;
7 |
8 | @ApiProperty()
9 | title: string;
10 |
11 | @ApiProperty()
12 | message: string;
13 |
14 | @ApiProperty({ enum: InAppNotificationType })
15 | type: InAppNotificationType;
16 |
17 | @ApiProperty()
18 | isRead: boolean;
19 |
20 | @ApiProperty()
21 | recipientUserId: number;
22 |
23 | @ApiProperty()
24 | isArchived: boolean;
25 |
26 | @ApiProperty()
27 | readAt: Date;
28 |
29 | @ApiProperty()
30 | archivedAt: Date;
31 |
32 | @ApiProperty()
33 | createdAt: Date;
34 |
35 | @ApiProperty()
36 | updatedAt: Date;
37 | }
--------------------------------------------------------------------------------
/backend/src/puzzle-test-case/index.ts:
--------------------------------------------------------------------------------
1 | // Main exports for the puzzle test case module
2 | export { PuzzleTestCaseModule } from "./puzzle-test-case.module"
3 | export { PuzzleTestCaseService } from "./services/puzzle-test-case.service"
4 | export { ValidationService } from "./services/validation.service"
5 | export { PuzzleTestCaseController } from "./controllers/puzzle-test-case.controller"
6 | export { ValidationController } from "./controllers/validation.controller"
7 | export { AdminGuard } from "./guards/admin.guard"
8 | export { PuzzleTestCase, TestCaseType, ValidationMode } from "./entities/puzzle-test-case.entity"
9 | export { ValidationResult, ValidationStatus } from "./entities/validation-result.entity"
10 |
11 | // Interface and DTO exports
12 | export * from "./interfaces/test-case.interface"
13 | export * from "./dto/test-case.dto"
14 |
--------------------------------------------------------------------------------
/backend/src/admin/strategies/jwt.strategy.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@nestjs/common';
2 | import { PassportStrategy } from '@nestjs/passport';
3 | import { ExtractJwt, Strategy } from 'passport-jwt';
4 | import { AdminService } from '../admin.service';
5 |
6 | @Injectable()
7 | export class JwtStrategy extends PassportStrategy(Strategy, 'admin-jwt') {
8 | constructor(private adminService: AdminService) {
9 | super({
10 | jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
11 | ignoreExpiration: false,
12 | secretOrKey: process.env.JWT_SECRET || 'supersecret',
13 | });
14 | }
15 |
16 | async validate(payload: any) {
17 | const admin = await this.adminService.findByEmail(payload.email);
18 | if (!admin) {
19 | return null;
20 | }
21 | return { ...admin, role: payload.role };
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/backend/src/puzzle-dependency/entities/puzzle-dependency.entity.ts:
--------------------------------------------------------------------------------
1 | import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, Index } from 'typeorm';
2 |
3 | @Entity('puzzle_dependencies')
4 | @Index(['puzzleId', 'dependsOnPuzzleId'], { unique: true })
5 | export class PuzzleDependency {
6 | @PrimaryGeneratedColumn('uuid')
7 | id: string;
8 |
9 | @Column({ name: 'puzzle_id' })
10 | @Index()
11 | puzzleId: string;
12 |
13 | @Column({ name: 'depends_on_puzzle_id' })
14 | @Index()
15 | dependsOnPuzzleId: string;
16 |
17 | @Column({ default: true })
18 | isRequired: boolean;
19 |
20 | @Column({ type: 'text', nullable: true })
21 | description: string;
22 |
23 | @CreateDateColumn({ name: 'created_at' })
24 | createdAt: Date;
25 |
26 | @UpdateDateColumn({ name: 'updated_at' })
27 | updatedAt: Date;
28 | }
--------------------------------------------------------------------------------
/frontend/components/Pagination.jsx:
--------------------------------------------------------------------------------
1 | export default function Pagination({ currentPage, totalPages, onPageChange }) {
2 | return (
3 |
4 |
11 |
12 | Page {currentPage} of {totalPages}
13 |
14 |
21 |
22 | );
23 | }
24 |
--------------------------------------------------------------------------------
/backend/src/streak/streak.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from "@nestjs/common"
2 | import { TypeOrmModule } from "@nestjs/typeorm"
3 | import { Streak } from "./entities/streak.entity"
4 | import { StreakActivity } from "./entities/streak-activity.entity"
5 | import { StreakCalculationService } from "./services/streak-calculation.service"
6 | import { StreakService } from "./services/streak.service"
7 | import { StreakController } from "./controllers/streak.controller"
8 | import { PublicStreakController } from "./controllers/public-streak.controller"
9 |
10 | @Module({
11 | imports: [TypeOrmModule.forFeature([Streak, StreakActivity])],
12 | controllers: [StreakController, PublicStreakController],
13 | providers: [StreakCalculationService, StreakService],
14 | exports: [StreakService, StreakCalculationService],
15 | })
16 | export class StreakModule {}
17 |
--------------------------------------------------------------------------------
/backend/src/user-inventory/entities/user.ts:
--------------------------------------------------------------------------------
1 | import { Entity, PrimaryGeneratedColumn, Column, OneToMany, CreateDateColumn, UpdateDateColumn } from 'typeorm';
2 | import { Inventory } from './inventory';
3 | import { TimeTrial } from 'src/time-trial/time-trial.entity';
4 |
5 | @Entity('users')
6 | export class User {
7 | @PrimaryGeneratedColumn('uuid')
8 | id: string;
9 |
10 | @Column({ unique: true })
11 | username: string;
12 |
13 | @Column({ unique: true })
14 | email: string;
15 |
16 | @Column()
17 | password: string;
18 |
19 | // @OneToMany(() => TimeTrial, timeTrial => timeTrial.user)
20 | // timeTrials: TimeTrial[];
21 |
22 | @OneToMany(() => Inventory, inventory => inventory.user)
23 | inventory: Inventory[];
24 |
25 | @CreateDateColumn()
26 | createdAt: Date;
27 |
28 | @UpdateDateColumn()
29 | updatedAt: Date;
30 | }
--------------------------------------------------------------------------------
/frontend/components/TestComponent.jsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { useQuery } from "@tanstack/react-query";
4 |
5 | function fetchData() {
6 | return fetch("https://jsonplaceholder.typicode.com/todos/1").then((res) =>
7 | res.json()
8 | );
9 | }
10 |
11 | export default function TestComponent() {
12 | const { data, error, isLoading } = useQuery({
13 | queryKey: ["testData"],
14 | queryFn: fetchData,
15 | });
16 |
17 | if (isLoading) return Loading...
;
18 | if (error) return Error fetching data
;
19 |
20 | return (
21 |
22 |
Fetched Data:
23 |
{JSON.stringify(data, null, 2)}
24 |
25 | );
26 | }
27 |
--------------------------------------------------------------------------------
/backend/src/user-ranking/user-ranking.controller.ts:
--------------------------------------------------------------------------------
1 | import { Controller, Get, Param } from '@nestjs/common';
2 | import { UserRankingService } from './user-ranking.service';
3 | import { UserRankDto } from './dto/create-user-ranking.dto';
4 | import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
5 |
6 | @ApiTags('User Ranking')
7 | @Controller('users')
8 | export class UserRankingController {
9 | constructor(private readonly userRankingService: UserRankingService) {}
10 |
11 | @Get(':id/rank')
12 | @ApiOperation({ summary: 'Get user rank' })
13 | @ApiResponse({ status: 200, description: 'Returns user rank data', type: UserRankDto })
14 | @ApiResponse({ status: 404, description: 'User not found' })
15 | async getUserRank(@Param('id') userId: string): Promise {
16 | return this.userRankingService.getUserRank(userId);
17 | }
18 | }
--------------------------------------------------------------------------------
/backend/src/puzzle-dependency/dto/create-puzzle-dependency.dto.ts:
--------------------------------------------------------------------------------
1 | import { IsString, IsBoolean, IsOptional, IsNotEmpty } from 'class-validator';
2 | import { ApiProperty } from '@nestjs/swagger';
3 |
4 | export class CreatePuzzleDependencyDto {
5 | @ApiProperty({ description: 'ID of the puzzle that has dependencies' })
6 | @IsString()
7 | @IsNotEmpty()
8 | puzzleId: string;
9 |
10 | @ApiProperty({ description: 'ID of the puzzle that must be completed first' })
11 | @IsString()
12 | @IsNotEmpty()
13 | dependsOnPuzzleId: string;
14 |
15 | @ApiProperty({ description: 'Whether this dependency is required', default: true })
16 | @IsBoolean()
17 | @IsOptional()
18 | isRequired?: boolean;
19 |
20 | @ApiProperty({ description: 'Optional description of the dependency', required: false })
21 | @IsString()
22 | @IsOptional()
23 | description?: string;
24 | }
--------------------------------------------------------------------------------
/backend/src/user-reaction/entities/reaction.entity.ts:
--------------------------------------------------------------------------------
1 | import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, Index, Unique } from "typeorm"
2 |
3 | @Entity("reactions")
4 | @Unique(["userId", "contentId"]) // Ensure one reaction per user per content
5 | @Index(["contentId"]) // Index for efficient aggregation queries
6 | @Index(["userId"]) // Index for user-specific queries
7 | export class Reaction {
8 | @PrimaryGeneratedColumn("uuid")
9 | id: string
10 |
11 | @Column({ type: "uuid" })
12 | userId: string
13 |
14 | @Column({ type: "varchar", length: 255 })
15 | contentId: string
16 |
17 | @Column({ type: "varchar", length: 10 })
18 | emoji: string
19 |
20 | @CreateDateColumn()
21 | createdAt: Date
22 |
23 | @Column({ type: "timestamp", default: () => "CURRENT_TIMESTAMP", onUpdate: "CURRENT_TIMESTAMP" })
24 | updatedAt: Date
25 | }
26 |
--------------------------------------------------------------------------------
/backend/src/quiz/entities/quiz-option.entity.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Entity,
3 | PrimaryGeneratedColumn,
4 | Column,
5 | ManyToOne,
6 | JoinColumn,
7 | } from 'typeorm';
8 | import { QuizQuestion } from './quiz-question.entity';
9 |
10 | @Entity('quiz_options')
11 | export class QuizOption {
12 | @PrimaryGeneratedColumn('uuid')
13 | id: string;
14 |
15 | @Column({ type: 'text' })
16 | text: string;
17 |
18 | @Column({ default: false })
19 | isCorrect: boolean;
20 |
21 | @Column({ type: 'int', default: 0 })
22 | order: number;
23 |
24 | @Column({ type: 'text', nullable: true })
25 | explanation: string;
26 |
27 | @ManyToOne(() => QuizQuestion, (question) => question.options, {
28 | onDelete: 'CASCADE',
29 | })
30 | @JoinColumn({ name: 'question_id' })
31 | question: QuizQuestion;
32 |
33 | @Column({ name: 'question_id' })
34 | questionId: string;
35 | }
36 |
--------------------------------------------------------------------------------
/backend/src/content/dto/create-content.dto.ts:
--------------------------------------------------------------------------------
1 | import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
2 | import { IsNotEmpty, IsOptional, IsString, MaxLength } from 'class-validator';
3 |
4 | export class CreateContentDto {
5 | @ApiProperty({ example: 'Introduction to Blockchain Technology' })
6 | @IsString()
7 | @IsNotEmpty()
8 | @MaxLength(255)
9 | title: string;
10 |
11 | @ApiProperty({ example: 'Blockchain is a distributed ledger technology that enables secure, transparent, and tamper-proof record-keeping...' })
12 | @IsString()
13 | @IsNotEmpty()
14 | body: string;
15 |
16 | @ApiProperty({ example: 'blockchain', description: 'Topic category for the content' })
17 | @IsString()
18 | @IsNotEmpty()
19 | @MaxLength(100)
20 | topic: string;
21 |
22 | @ApiPropertyOptional({ example: true, default: true })
23 | @IsOptional()
24 | isActive?: boolean;
25 | }
--------------------------------------------------------------------------------
/frontend/components/ui/input.jsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 |
3 | import { cn } from "../../lib/utils"
4 |
5 | const Input = React.forwardRef(({ className, type, ...props }, ref) => {
6 | return (
7 | ()
15 | );
16 | })
17 | Input.displayName = "Input"
18 |
19 | export { Input }
20 |
--------------------------------------------------------------------------------
/backend/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "declaration": true,
5 | "removeComments": true,
6 | "emitDecoratorMetadata": true,
7 | "experimentalDecorators": true,
8 | "allowSyntheticDefaultImports": true,
9 | "target": "ES2021",
10 | "sourceMap": true,
11 | "outDir": "./dist",
12 | "baseUrl": "./",
13 | "incremental": true,
14 | "skipLibCheck": true,
15 | "strictNullChecks": false,
16 | "noImplicitAny": false,
17 | "strictBindCallApply": false,
18 | "forceConsistentCasingInFileNames": false,
19 | "noFallthroughCasesInSwitch": false,
20 | "esModuleInterop": true,
21 | "resolveJsonModule": true,
22 | "typeRoots": ["node_modules/@types"],
23 | "types": ["node", "jest"]
24 | },
25 | "include": ["src/**/*", "config/**/*", "test/**/*"],
26 | "exclude": ["node_modules", "dist"]
27 | }
28 |
--------------------------------------------------------------------------------
/backend/src/hint/hint.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@nestjs/common';
2 | import { InjectRepository } from '@nestjs/typeorm';
3 | import { Hint } from './hint.entity';
4 | import { Repository } from 'typeorm';
5 |
6 | @Injectable()
7 | export class HintService {
8 | constructor(
9 | @InjectRepository(Hint)
10 | private hintRepository: Repository,
11 | ) {}
12 |
13 | async create(data: Partial) {
14 | const hint = this.hintRepository.create(data);
15 | return this.hintRepository.save(hint);
16 | }
17 |
18 | async getAvailableHints(
19 | puzzleId: string,
20 | elapsedMinutes: number,
21 | ): Promise {
22 | const hints = await this.hintRepository.find({
23 | where: { puzzleId },
24 | order: { unlockTimeInMinutes: 'ASC' },
25 | });
26 |
27 | return hints.filter((hint) => hint.unlockTimeInMinutes <= elapsedMinutes);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/backend/src/admin/guards/roles.guard.ts:
--------------------------------------------------------------------------------
1 | import { Injectable, CanActivate, ExecutionContext, ForbiddenException } from '@nestjs/common';
2 | import { Reflector } from '@nestjs/core';
3 | import { ROLES_KEY } from '../roles.decorator';
4 | import { AdminRole } from '../admin-role.enum';
5 |
6 | @Injectable()
7 | export class RolesGuard implements CanActivate {
8 | constructor(private reflector: Reflector) {}
9 |
10 | canActivate(context: ExecutionContext): boolean {
11 | const requiredRoles = this.reflector.getAllAndOverride(ROLES_KEY, [
12 | context.getHandler(),
13 | context.getClass(),
14 | ]);
15 | if (!requiredRoles) {
16 | return true;
17 | }
18 | const { user } = context.switchToHttp().getRequest();
19 | if (!user || !requiredRoles.includes(user.role)) {
20 | throw new ForbiddenException('Insufficient role');
21 | }
22 | return true;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/backend/src/nft-claim/nft-claim.controller.ts:
--------------------------------------------------------------------------------
1 | import { Controller, Post, Body, HttpCode, HttpStatus } from '@nestjs/common';
2 | import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger';
3 | import { NFTClaimService } from './nft-claim.service';
4 | import { ClaimNFTDto } from './dto/claim-nft.dto';
5 |
6 | @ApiTags('NFT Claim')
7 | @Controller('nft-claim')
8 | export class NFTClaimController {
9 | constructor(private readonly nftClaimService: NFTClaimService) {}
10 |
11 | @Post('claim')
12 | @HttpCode(HttpStatus.OK)
13 | @ApiOperation({ summary: 'Claim an NFT' })
14 | @ApiResponse({ status: 200, description: 'NFT claimed successfully.' })
15 | @ApiResponse({ status: 400, description: 'Bad request.' })
16 | @ApiResponse({ status: 500, description: 'Internal server error.' })
17 | async claimNFT(@Body() claimNFTDto: ClaimNFTDto) {
18 | return this.nftClaimService.claimNFT(claimNFTDto);
19 | }
20 | }
--------------------------------------------------------------------------------
/backend/src/hint/hint.controller.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Controller,
3 | Get,
4 | Param,
5 | Query,
6 | BadRequestException,
7 | } from '@nestjs/common';
8 | import { HintService } from './hint.service';
9 |
10 | @Controller('hints')
11 | export class HintController {
12 | constructor(private readonly hintService: HintService) {}
13 |
14 | @Get(':puzzleId')
15 | async getHints(
16 | @Param('puzzleId') puzzleId: string,
17 | @Query('startTime') startTime: string,
18 | ) {
19 | if (!startTime)
20 | throw new BadRequestException('startTime query param is required');
21 |
22 | const start = new Date(startTime).getTime();
23 | const now = Date.now();
24 | if (isNaN(start)) throw new BadRequestException('Invalid startTime format');
25 |
26 | const elapsedMinutes = Math.floor((now - start) / 60000);
27 | return this.hintService.getAvailableHints(puzzleId, elapsedMinutes);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/backend/src/in-app-notifications/dto/create-notification.dto.ts:
--------------------------------------------------------------------------------
1 | import { IsString, IsEnum, IsOptional, IsNumber, IsNotEmpty } from 'class-validator';
2 | import { ApiProperty } from '@nestjs/swagger';
3 | import { InAppNotificationType } from '../entities/in-app-notification.entity';
4 |
5 | export class CreateNotificationDto {
6 | @ApiProperty({ description: 'Title of the notification' })
7 | @IsString()
8 | @IsNotEmpty()
9 | title: string;
10 |
11 | @ApiProperty({ description: 'Message content of the notification' })
12 | @IsString()
13 | @IsNotEmpty()
14 | message: string;
15 |
16 | @ApiProperty({ description: 'Type of notification', enum: InAppNotificationType })
17 | @IsEnum(InAppNotificationType)
18 | type: InAppNotificationType;
19 |
20 | @ApiProperty({ description: 'ID of the user who should receive this notification', required: false })
21 | @IsNumber()
22 | @IsOptional()
23 | userId?: number;
24 | }
--------------------------------------------------------------------------------
/backend/src/migration/dto/migration.dto.ts:
--------------------------------------------------------------------------------
1 | import { ApiProperty } from "@nestjs/swagger"
2 | import { IsString, IsOptional, IsNumber, Min } from "class-validator"
3 | import type { Express } from "express"
4 |
5 | export class ParseJsonDto {
6 | @ApiProperty({
7 | description: "JSON string containing puzzle data",
8 | example: '{"puzzles": [{"title": "Sample Puzzle", "difficulty": "easy", ...}]}',
9 | })
10 | @IsString()
11 | jsonData: string
12 | }
13 |
14 | export class CleanupDto {
15 | @ApiProperty({
16 | description: "Number of days old for cleanup threshold",
17 | example: 30,
18 | required: false,
19 | })
20 | @IsOptional()
21 | @IsNumber()
22 | @Min(1)
23 | daysOld?: number
24 | }
25 |
26 | export class UploadFileDto {
27 | @ApiProperty({
28 | type: "string",
29 | format: "binary",
30 | description: "JSON file containing puzzle data",
31 | })
32 | file: Express.Multer.File
33 | }
34 |
--------------------------------------------------------------------------------
/backend/src/progress/progress.controller.ts:
--------------------------------------------------------------------------------
1 | import { Controller, Get, Param, ParseUUIDPipe } from '@nestjs/common';
2 | import { ProgressService } from './progress.service';
3 | import { ProgressResponseDto } from './dto/progress-response.dto';
4 | import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
5 |
6 | @ApiTags('Progress')
7 | @Controller('users')
8 | export class ProgressController {
9 | constructor(private readonly progressService: ProgressService) {}
10 |
11 | @Get(':id/progress')
12 | @ApiOperation({ summary: 'Get user progress' })
13 | @ApiResponse({ status: 200, description: 'Progress retrieved', type: ProgressResponseDto })
14 | @ApiResponse({ status: 404, description: 'User progress not found' })
15 | async getUserProgress(
16 | @Param('id', new ParseUUIDPipe()) userId: string,
17 | ): Promise {
18 | return this.progressService.getProgressByUserId(userId);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/backend/src/gameMechanics/entities/hint-usage.entity.ts:
--------------------------------------------------------------------------------
1 | import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, ManyToOne, JoinColumn } from "typeorm"
2 | import { User } from "./user.entity"
3 | import { Challenge } from "./challenge.entity"
4 |
5 | @Entity("hint_usages")
6 | export class HintUsage {
7 | @PrimaryGeneratedColumn("uuid")
8 | id: string
9 |
10 | @Column("uuid")
11 | userId: string
12 |
13 | @Column("uuid")
14 | challengeId: string
15 |
16 | @Column({ type: "int" })
17 | hintIndex: number
18 |
19 | @Column("text")
20 | hintContent: string
21 |
22 | @CreateDateColumn()
23 | usedAt: Date
24 |
25 | @ManyToOne(
26 | () => User,
27 | (user) => user.hintUsages,
28 | )
29 | @JoinColumn({ name: "userId" })
30 | user: User
31 |
32 | @ManyToOne(
33 | () => Challenge,
34 | (challenge) => challenge.hintUsages,
35 | )
36 | @JoinColumn({ name: "challengeId" })
37 | challenge: Challenge
38 | }
39 |
--------------------------------------------------------------------------------
/frontend/lib/authOptions.ts:
--------------------------------------------------------------------------------
1 | // lib/authOptions.ts
2 | import GitHubProvider from "next-auth/providers/github";
3 | import TwitterProvider from "next-auth/providers/twitter";
4 | import DiscordProvider from "next-auth/providers/discord";
5 |
6 | export const authOptions = {
7 | providers: [
8 | GitHubProvider({
9 | clientId: process.env.GITHUB_CLIENT_ID!,
10 | clientSecret: process.env.GITHUB_CLIENT_SECRET!,
11 | }),
12 | TwitterProvider({
13 | clientId: process.env.TWITTER_CLIENT_ID!,
14 | clientSecret: process.env.TWITTER_CLIENT_SECRET!,
15 | version: "2.0", // Twitter OAuth 2.0
16 | }),
17 | DiscordProvider({
18 | clientId: process.env.DISCORD_CLIENT_ID!,
19 | clientSecret: process.env.DISCORD_CLIENT_SECRET!,
20 | }),
21 | ],
22 | callbacks: {
23 | async signIn({ user, account, profile }) {
24 | // Optionally persist or associate account data here
25 | return true;
26 | },
27 | },
28 | };
29 |
--------------------------------------------------------------------------------
/backend/src/auth/exceptions/auth.exceptions.ts:
--------------------------------------------------------------------------------
1 | import { HttpException, HttpStatus } from "@nestjs/common"
2 |
3 | export class InvalidCredentialsException extends HttpException {
4 | constructor() {
5 | super("Invalid email or password", HttpStatus.UNAUTHORIZED)
6 | }
7 | }
8 |
9 | export class UserAlreadyExistsException extends HttpException {
10 | constructor() {
11 | super("User with this email already exists", HttpStatus.CONFLICT)
12 | }
13 | }
14 |
15 | export class AccountDeactivatedException extends HttpException {
16 | constructor() {
17 | super("Account has been deactivated", HttpStatus.UNAUTHORIZED)
18 | }
19 | }
20 |
21 | export class TokenExpiredException extends HttpException {
22 | constructor() {
23 | super("Token has expired", HttpStatus.UNAUTHORIZED)
24 | }
25 | }
26 |
27 | export class InvalidTokenException extends HttpException {
28 | constructor() {
29 | super("Invalid token provided", HttpStatus.UNAUTHORIZED)
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/backend/src/user-reaction/dto/create-reaction.dto.ts:
--------------------------------------------------------------------------------
1 | import { IsString, IsNotEmpty, IsUUID, Matches } from "class-validator"
2 | import { ApiProperty } from "@nestjs/swagger"
3 |
4 | export class CreateReactionDto {
5 | @ApiProperty({
6 | description: "User ID who is reacting",
7 | example: "123e4567-e89b-12d3-a456-426614174000",
8 | })
9 | @IsUUID()
10 | @IsNotEmpty()
11 | userId: string
12 |
13 | @ApiProperty({
14 | description: "Content ID being reacted to",
15 | example: "puzzle-123",
16 | })
17 | @IsString()
18 | @IsNotEmpty()
19 | contentId: string
20 |
21 | @ApiProperty({
22 | description: "Emoji reaction",
23 | example: "👍",
24 | enum: ["👍", "👎", "❤️", "😂", "😮", "😢", "😡", "🤔", "🎉", "🔥"],
25 | })
26 | @IsString()
27 | @IsNotEmpty()
28 | @Matches(/^(👍|👎|❤️|😂|😮|😢|😡|🤔|🎉|🔥)$/, {
29 | message: "Invalid emoji. Allowed emojis: 👍, 👎, ❤️, 😂, 😮, 😢, 😡, 🤔, 🎉, 🔥",
30 | })
31 | emoji: string
32 | }
33 |
--------------------------------------------------------------------------------
/backend/.gitignore:
--------------------------------------------------------------------------------
1 | # compiled output
2 | /dist
3 | /node_modules
4 | /build
5 |
6 | # Logs
7 | logs
8 | *.log
9 | npm-debug.log*
10 | pnpm-debug.log*
11 | yarn-debug.log*
12 | yarn-error.log*
13 | lerna-debug.log*
14 |
15 | # OS
16 | .DS_Store
17 |
18 | # Tests
19 | /coverage
20 | /.nyc_output
21 |
22 | # IDEs and editors
23 | /.idea
24 | .project
25 | .classpath
26 | .c9/
27 | *.launch
28 | .settings/
29 | *.sublime-workspace
30 |
31 | # IDE - VSCode
32 | .vscode/*
33 | !.vscode/settings.json
34 | !.vscode/tasks.json
35 | !.vscode/launch.json
36 | !.vscode/extensions.json
37 |
38 | # dotenv environment variable files
39 | .env*
40 | .env.development.local
41 | .env.test.local
42 | .env.production.local
43 | .env.local
44 |
45 | # temp directory
46 | .temp
47 | .tmp
48 |
49 | # Runtime data
50 | pids
51 | *.pid
52 | *.seed
53 | *.pid.lock
54 |
55 | # Diagnostic reports (https://nodejs.org/api/report.html)
56 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
57 |
58 | *.IP2LOCATION-LITE.BIN
59 |
--------------------------------------------------------------------------------
/backend/src/auth/dto/auth-response.dto.ts:
--------------------------------------------------------------------------------
1 | import { ApiProperty } from "@nestjs/swagger"
2 |
3 | // Step 1: Create a nested DTO for the user
4 | export class UserDto {
5 | @ApiProperty()
6 | id: string
7 |
8 | @ApiProperty()
9 | name: string
10 |
11 | @ApiProperty()
12 | email: string
13 |
14 | @ApiProperty()
15 | createdAt: Date
16 | }
17 |
18 | // Step 2: Use UserDto in your response
19 | export class AuthResponseDto {
20 | @ApiProperty({
21 | description: "JWT access token",
22 | example: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
23 | })
24 | accessToken: string
25 |
26 | @ApiProperty({
27 | description: "Token type",
28 | example: "Bearer",
29 | })
30 | tokenType: string
31 |
32 | @ApiProperty({
33 | description: "Token expiration time in seconds",
34 | example: 900,
35 | })
36 | expiresIn: number
37 |
38 | @ApiProperty({
39 | description: "User information",
40 | type: () => UserDto,
41 | })
42 | user: UserDto
43 | }
44 |
--------------------------------------------------------------------------------
/backend/src/session/session.controller.ts:
--------------------------------------------------------------------------------
1 | import { Controller, Post, Body, Param, Get } from '@nestjs/common';
2 | import { SessionService } from './session.service';
3 | import { Session } from './session.entity';
4 | import { ActivityType } from './enum/activityType.enum';
5 |
6 | @Controller('sessions')
7 | export class SessionController {
8 | constructor(private readonly sessionService: SessionService) {}
9 |
10 | @Post('start')
11 | async startSession(
12 | @Body('userId') userId: string,
13 | @Body('activityType') activityType: ActivityType,
14 | ): Promise {
15 | return this.sessionService.startSession(userId, activityType);
16 | }
17 |
18 | @Post('end/:sessionId')
19 | async endSession(
20 | @Param('sessionId') sessionId: string,
21 | ): Promise {
22 | return this.sessionService.endSession(sessionId);
23 | }
24 |
25 | @Get('active')
26 | async getActiveSessions(): Promise {
27 | return this.sessionService.getActiveSessions();
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/backend/src/puzzle-review/puzzle-review/puzzle-review.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from "@nestjs/common"
2 | import { TypeOrmModule } from "@nestjs/typeorm"
3 | import { ConfigModule } from "@nestjs/config"
4 | import { PuzzleReviewService } from "./services/puzzle-review.service"
5 | import { ModerationService } from "./services/moderation.service"
6 | import { PuzzleReviewController } from "./controllers/puzzle-review.controller"
7 | import { ModerationController } from "./controllers/moderation.controller"
8 | import { PuzzleReview } from "./entities/puzzle-review.entity"
9 | import { ReviewModeration } from "./entities/review-moderation.entity"
10 | import { AdminGuard } from "./guards/admin.guard"
11 |
12 | @Module({
13 | imports: [ConfigModule, TypeOrmModule.forFeature([PuzzleReview, ReviewModeration])],
14 | providers: [PuzzleReviewService, ModerationService, AdminGuard],
15 | controllers: [PuzzleReviewController, ModerationController],
16 | exports: [PuzzleReviewService, ModerationService],
17 | })
18 | export class PuzzleReviewModule {}
19 |
--------------------------------------------------------------------------------
/backend/src/audit-log/interceptor/audit-log.interceptor.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Injectable,
3 | NestInterceptor,
4 | ExecutionContext,
5 | CallHandler,
6 | } from '@nestjs/common';
7 | import { AuditLogService } from '../audit-log.service';
8 | import { Observable, tap } from 'rxjs';
9 |
10 | @Injectable()
11 | export class AuditLogInterceptor implements NestInterceptor {
12 | constructor(private readonly auditLogService: AuditLogService) {}
13 |
14 | intercept(context: ExecutionContext, next: CallHandler): Observable {
15 | const req = context.switchToHttp().getRequest();
16 | const user = req.user;
17 |
18 | return next.handle().pipe(
19 | tap(async () => {
20 | if (user) {
21 | const action = `${req.method} ${req.originalUrl}`;
22 | const meta = {
23 | body: req.body,
24 | params: req.params,
25 | query: req.query,
26 | };
27 | await this.auditLogService.createLog(user.id, action, meta);
28 | }
29 | }),
30 | );
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/backend/src/puzzle-submission/puzzle-submission.controller.ts:
--------------------------------------------------------------------------------
1 | import { Controller, Post, Body, UseGuards } from '@nestjs/common';
2 | import { PuzzleSubmissionService } from './puzzle-submission.service';
3 | import { RateLimit } from 'src/rate-limiter/rate-limit.decorator';
4 | import { RateLimitGuard } from 'src/rate-limiter/rate-limit.guard';
5 |
6 | @Controller('puzzle-submission')
7 | export class PuzzleSubmissionController {
8 | constructor(private readonly submissionService: PuzzleSubmissionService) {}
9 |
10 | @Post()
11 | @UseGuards(RateLimitGuard)
12 | @RateLimit({ ttl: 60, limit: 5 })
13 | async submit(
14 | @Body()
15 | body: {
16 | playerId: string;
17 | puzzleId: string;
18 | answer: string;
19 | correctAnswer: string;
20 | },
21 | ) {
22 | const { playerId, puzzleId, answer, correctAnswer } = body;
23 | const result = await this.submissionService.submitAnswer(
24 | playerId,
25 | puzzleId,
26 | answer,
27 | correctAnswer,
28 | );
29 | return result;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/backend/src/puzzle-test-case/puzzle-test-case.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from "@nestjs/common"
2 | import { TypeOrmModule } from "@nestjs/typeorm"
3 | import { ConfigModule } from "@nestjs/config"
4 | import { PuzzleTestCaseService } from "./services/puzzle-test-case.service"
5 | import { ValidationService } from "./services/validation.service"
6 | import { PuzzleTestCaseController } from "./controllers/puzzle-test-case.controller"
7 | import { ValidationController } from "./controllers/validation.controller"
8 | import { PuzzleTestCase } from "./entities/puzzle-test-case.entity"
9 | import { ValidationResult } from "./entities/validation-result.entity"
10 | import { AdminGuard } from "./guards/admin.guard"
11 |
12 | @Module({
13 | imports: [ConfigModule, TypeOrmModule.forFeature([PuzzleTestCase, ValidationResult])],
14 | providers: [PuzzleTestCaseService, ValidationService, AdminGuard],
15 | controllers: [PuzzleTestCaseController, ValidationController],
16 | exports: [PuzzleTestCaseService, ValidationService],
17 | })
18 | export class PuzzleTestCaseModule {}
19 |
--------------------------------------------------------------------------------
/frontend/components/GradientButton.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Loader2 } from "lucide-react";
3 |
4 | const GradientButton = ({
5 | type = "button",
6 | onClick,
7 | disabled = false,
8 | isLoading = false,
9 | icon: Icon,
10 | children,
11 | className = "",
12 | }) => {
13 | return (
14 |
37 | );
38 | };
39 |
40 | export default GradientButton;
41 |
--------------------------------------------------------------------------------
/backend/src/auth/middleware/auth.middleware.ts:
--------------------------------------------------------------------------------
1 | import { Injectable, type NestMiddleware, UnauthorizedException } from "@nestjs/common"
2 | import type { Request, Response, NextFunction } from "express"
3 | import type { JwtService } from "@nestjs/jwt"
4 | import type { ConfigService } from "@nestjs/config"
5 |
6 | @Injectable()
7 | export class AuthMiddleware implements NestMiddleware {
8 | constructor(
9 | private jwtService: JwtService,
10 | private configService: ConfigService,
11 | ) {}
12 |
13 | use(req: Request, res: Response, next: NextFunction) {
14 | const authHeader = req.headers.authorization
15 |
16 | if (authHeader && authHeader.startsWith("Bearer ")) {
17 | const token = authHeader.substring(7)
18 |
19 | try {
20 | const decoded = this.jwtService.verify(token, {
21 | secret: this.configService.get("JWT_SECRET"),
22 | })
23 |
24 | req["user"] = decoded
25 | } catch (error) {
26 | throw new UnauthorizedException("Invalid token")
27 | }
28 | }
29 |
30 | next()
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/backend/src/auth/strategies/jwt.strategy.ts:
--------------------------------------------------------------------------------
1 | import { Injectable, UnauthorizedException } from "@nestjs/common"
2 | import { PassportStrategy } from "@nestjs/passport"
3 | import { ExtractJwt, Strategy } from "passport-jwt"
4 | import { ConfigService } from "@nestjs/config"
5 | import { User } from "../entities/user.entity"
6 | import { AuthService, JwtPayload } from "../services/auth.service"
7 |
8 | @Injectable()
9 | export class JwtStrategy extends PassportStrategy(Strategy) {
10 | constructor(
11 | private authService: AuthService,
12 | private configService: ConfigService,
13 | ) {
14 | super({
15 | jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
16 | ignoreExpiration: false,
17 | secretOrKey: configService.get("JWT_SECRET") || "your-secret-key",
18 | })
19 | }
20 |
21 | async validate(payload: JwtPayload): Promise {
22 | try {
23 | const user = await this.authService.validateUser(payload)
24 | return user
25 | } catch (error) {
26 | throw new UnauthorizedException("Invalid token")
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/backend/src/referral/referral.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from "@nestjs/common"
2 | import { TypeOrmModule } from "@nestjs/typeorm"
3 | import { ReferralCode } from "./entities/referral-code.entity"
4 | import { ReferralInvite } from "./entities/referral-invite.entity"
5 | import { ReferralBonus } from "./entities/referral-bonus.entity"
6 | import { ReferralCodeService } from "./services/referral-code.service"
7 | import { ReferralInviteService } from "./services/referral-invite.service"
8 | import { ReferralBonusService } from "./services/referral-bonus.service"
9 | import { ReferralService } from "./services/referral.service"
10 | import { ReferralController } from "./controllers/referral.controller"
11 |
12 | @Module({
13 | imports: [TypeOrmModule.forFeature([ReferralCode, ReferralInvite, ReferralBonus])],
14 | controllers: [ReferralController],
15 | providers: [ReferralCodeService, ReferralInviteService, ReferralBonusService, ReferralService],
16 | exports: [ReferralService, ReferralCodeService, ReferralInviteService, ReferralBonusService],
17 | })
18 | export class ReferralModule {}
19 |
--------------------------------------------------------------------------------
/backend/src/feedback/guards/admin.guard.ts:
--------------------------------------------------------------------------------
1 | import { Injectable, type CanActivate, type ExecutionContext, UnauthorizedException, Logger } from "@nestjs/common"
2 |
3 | @Injectable()
4 | export class AdminGuard implements CanActivate {
5 | private readonly logger = new Logger(AdminGuard.name)
6 |
7 | canActivate(context: ExecutionContext): boolean {
8 | const request = context.switchToHttp().getRequest()
9 |
10 | // Check for admin role in user object (assuming it's set by auth middleware)
11 | const user = request.user
12 | if (!user) {
13 | this.logger.warn("No user found in request")
14 | throw new UnauthorizedException("Authentication required")
15 | }
16 |
17 | // Check if user has admin role
18 | if (!user.roles?.includes("admin") && user.role !== "admin") {
19 | this.logger.warn(`User ${user.id} attempted to access admin endpoint without proper permissions`)
20 | throw new UnauthorizedException("Admin access required")
21 | }
22 |
23 | this.logger.log(`Admin access granted to user ${user.id}`)
24 | return true
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/backend/src/migration/guards/admin.guard.ts:
--------------------------------------------------------------------------------
1 | import { Injectable, type CanActivate, type ExecutionContext, UnauthorizedException, Logger } from "@nestjs/common"
2 |
3 | @Injectable()
4 | export class AdminGuard implements CanActivate {
5 | private readonly logger = new Logger(AdminGuard.name)
6 |
7 | canActivate(context: ExecutionContext): boolean {
8 | const request = context.switchToHttp().getRequest()
9 |
10 | // Check for admin role in user object (assuming it's set by auth middleware)
11 | const user = request.user
12 | if (!user) {
13 | this.logger.warn("No user found in request")
14 | throw new UnauthorizedException("Authentication required")
15 | }
16 |
17 | // Check if user has admin role
18 | if (!user.roles?.includes("admin") && user.role !== "admin") {
19 | this.logger.warn(`User ${user.id} attempted to access admin endpoint without proper permissions`)
20 | throw new UnauthorizedException("Admin access required")
21 | }
22 |
23 | this.logger.log(`Admin access granted to user ${user.id}`)
24 | return true
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/backend/src/puzzle/dto/create-puzzle.dto.ts:
--------------------------------------------------------------------------------
1 | import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
2 | import { IsNotEmpty, IsOptional, IsString, MaxLength } from 'class-validator';
3 |
4 | export class CreatePuzzleDto {
5 | @ApiProperty({ example: 'Puzzle Title' })
6 | @IsString()
7 | @IsNotEmpty()
8 | @MaxLength(255)
9 | title: string;
10 |
11 | @ApiProperty({ example: 'Puzzle description' })
12 | @IsString()
13 | @IsNotEmpty()
14 | description: string;
15 |
16 | @ApiProperty({ example: 'easy', enum: ['easy', 'medium', 'hard'] })
17 | @IsString()
18 | @IsNotEmpty()
19 | difficulty: string;
20 |
21 | @ApiPropertyOptional({ example: 'This is a hint' })
22 | @IsOptional()
23 | @IsString()
24 | hint?: string;
25 |
26 | @ApiProperty({ example: 'solution123' })
27 | @IsString()
28 | @IsNotEmpty()
29 | solution: string;
30 |
31 | @ApiPropertyOptional({ example: 'reward-uuid' })
32 | @IsOptional()
33 | @IsString()
34 | rewardId?: string;
35 |
36 | @ApiPropertyOptional({ example: true })
37 | @IsOptional()
38 | isActive?: boolean;
39 | }
40 |
--------------------------------------------------------------------------------
/backend/src/puzzle-test-case/guards/admin.guard.ts:
--------------------------------------------------------------------------------
1 | import { Injectable, type CanActivate, type ExecutionContext, UnauthorizedException, Logger } from "@nestjs/common"
2 |
3 | @Injectable()
4 | export class AdminGuard implements CanActivate {
5 | private readonly logger = new Logger(AdminGuard.name)
6 |
7 | canActivate(context: ExecutionContext): boolean {
8 | const request = context.switchToHttp().getRequest()
9 |
10 | // Check for admin role in user object (assuming it's set by auth middleware)
11 | const user = request.user
12 | if (!user) {
13 | this.logger.warn("No user found in request")
14 | throw new UnauthorizedException("Authentication required")
15 | }
16 |
17 | // Check if user has admin role
18 | if (!user.roles?.includes("admin") && user.role !== "admin") {
19 | this.logger.warn(`User ${user.id} attempted to access admin endpoint without proper permissions`)
20 | throw new UnauthorizedException("Admin access required")
21 | }
22 |
23 | this.logger.log(`Admin access granted to user ${user.id}`)
24 | return true
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/backend/src/puzzle-versioning/puzzle-versioning.controller.ts:
--------------------------------------------------------------------------------
1 | import { Controller, Get, Post, Body, Param, UsePipes, ValidationPipe } from '@nestjs/common';
2 | import { PuzzleVersioningService } from './puzzle-versioning.service';
3 | import { CreatePuzzleVersionDto } from './dto/create-puzzle-version.dto';
4 |
5 | @Controller('puzzles')
6 | export class PuzzleVersioningController {
7 | constructor(private readonly puzzleVersioningService: PuzzleVersioningService) {}
8 |
9 |
10 | @Get(':puzzleId/latest')
11 | findLatestVersion(@Param('puzzleId') puzzleId: string) {
12 | return this.puzzleVersioningService.findLatestVersion(puzzleId);
13 | }
14 |
15 |
16 | @Post('versions')
17 | @UsePipes(new ValidationPipe({ transform: true }))
18 | createNewVersion(@Body() createPuzzleVersionDto: CreatePuzzleVersionDto) {
19 | return this.puzzleVersioningService.createNewVersion(createPuzzleVersionDto);
20 | }
21 |
22 | @Get(':puzzleId/versions')
23 | findAllVersions(@Param('puzzleId') puzzleId: string) {
24 | return this.puzzleVersioningService.findAllVersions(puzzleId);
25 | }
26 | }
--------------------------------------------------------------------------------
/backend/src/user-token-history/guards/admin.guard.ts:
--------------------------------------------------------------------------------
1 | import { Injectable, type CanActivate, type ExecutionContext, UnauthorizedException, Logger } from "@nestjs/common"
2 |
3 | @Injectable()
4 | export class AdminGuard implements CanActivate {
5 | private readonly logger = new Logger(AdminGuard.name)
6 |
7 | canActivate(context: ExecutionContext): boolean {
8 | const request = context.switchToHttp().getRequest()
9 |
10 | // Check for admin role in user object (assuming it's set by auth middleware)
11 | const user = request.user
12 | if (!user) {
13 | this.logger.warn("No user found in request")
14 | throw new UnauthorizedException("Authentication required")
15 | }
16 |
17 | // Check if user has admin role
18 | if (!user.roles?.includes("admin") && user.role !== "admin") {
19 | this.logger.warn(`User ${user.id} attempted to access admin endpoint without proper permissions`)
20 | throw new UnauthorizedException("Admin access required")
21 | }
22 |
23 | this.logger.log(`Admin access granted to user ${user.id}`)
24 | return true
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/backend/src/puzzle-review/puzzle-review/guards/admin.guard.ts:
--------------------------------------------------------------------------------
1 | import { Injectable, type CanActivate, type ExecutionContext, UnauthorizedException, Logger } from "@nestjs/common"
2 |
3 | @Injectable()
4 | export class AdminGuard implements CanActivate {
5 | private readonly logger = new Logger(AdminGuard.name)
6 |
7 | canActivate(context: ExecutionContext): boolean {
8 | const request = context.switchToHttp().getRequest()
9 |
10 | // Check for admin role in user object (assuming it's set by auth middleware)
11 | const user = request.user
12 | if (!user) {
13 | this.logger.warn("No user found in request")
14 | throw new UnauthorizedException("Authentication required")
15 | }
16 |
17 | // Check if user has admin role
18 | if (!user.roles?.includes("admin") && user.role !== "admin") {
19 | this.logger.warn(`User ${user.id} attempted to access admin endpoint without proper permissions`)
20 | throw new UnauthorizedException("Admin access required")
21 | }
22 |
23 | this.logger.log(`Admin access granted to user ${user.id}`)
24 | return true
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/backend/src/user-report-card/entities/user-report-card.entity.ts:
--------------------------------------------------------------------------------
1 | import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from 'typeorm';
2 |
3 | @Entity('report_cards')
4 | export class ReportCard {
5 | @PrimaryGeneratedColumn('uuid')
6 | id: string;
7 |
8 | @Column({ unique: true })
9 | userId: string;
10 |
11 | @Column('int', { default: 0 })
12 | completedPuzzles: number;
13 |
14 | @Column('int', { default: 0 })
15 | rewardsEarned: number;
16 |
17 | @Column('decimal', { precision: 5, scale: 2, default: 0 })
18 | progressPercentage: number;
19 |
20 | @Column('int', { default: 0 })
21 | totalTimeSpent: number; // in minutes
22 |
23 | @Column('int', { default: 0 })
24 | streakDays: number;
25 |
26 | @Column('json', { nullable: true })
27 | categoryBreakdown: {
28 | beginner: number;
29 | intermediate: number;
30 | advanced: number;
31 | };
32 |
33 | @Column('json', { nullable: true })
34 | recentAchievements: string[];
35 |
36 | @CreateDateColumn()
37 | createdAt: Date;
38 |
39 | @UpdateDateColumn()
40 | updatedAt: Date;
41 | }
--------------------------------------------------------------------------------
/backend/src/wallet/wallet.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@nestjs/common';
2 | import { InjectRepository } from '@nestjs/typeorm';
3 | import { Repository } from 'typeorm';
4 | import { Wallet } from './entities/wallet.entity';
5 |
6 | @Injectable()
7 | export class WalletService {
8 | constructor(
9 | @InjectRepository(Wallet)
10 | private readonly walletRepository: Repository,
11 | ) {}
12 |
13 | async linkWallet(address: string): Promise {
14 | let wallet = await this.walletRepository.findOne({ where: { address } });
15 | if (!wallet) {
16 | wallet = this.walletRepository.create({ address });
17 | await this.walletRepository.save(wallet);
18 | }
19 | return wallet;
20 | }
21 |
22 | // Mock signature verification
23 | async verifySignature(address: string, signature: string, message: string): Promise {
24 | // Stub: Always returns true for now
25 | return true;
26 | }
27 |
28 | async findByAddress(address: string): Promise {
29 | return this.walletRepository.findOne({ where: { address } });
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/frontend/app/layout.js:
--------------------------------------------------------------------------------
1 | import localFont from "next/font/local";
2 | import "./globals.css";
3 |
4 | // import StoreProvider from "@/store/StoreProvider";
5 | import Providers from "@/lib/queryClient";
6 | import Navbar from "@/components/Navbar";
7 |
8 | const geistSans = localFont({
9 | src: "./fonts/GeistVF.woff",
10 | variable: "--font-geist-sans",
11 | weight: "100 900",
12 | });
13 | const geistMono = localFont({
14 | src: "./fonts/GeistMonoVF.woff",
15 | variable: "--font-geist-mono",
16 | weight: "100 900",
17 | });
18 |
19 | export const metadata = {
20 | title: "NFT Scavenger Hunt",
21 | description: "Solve puzzles and claim NFT rewards!",
22 | };
23 |
24 | export default function RootLayout({ children }) {
25 | return (
26 |
27 |
30 |
31 | {/* */}
32 |
33 | {children}
34 | {/* */}
35 |
36 |
37 |
38 | );
39 | }
40 |
--------------------------------------------------------------------------------
/backend/src/api-key/api-key.guard.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Injectable,
3 | CanActivate,
4 | ExecutionContext,
5 | UnauthorizedException,
6 | Logger,
7 | } from '@nestjs/common';
8 | import { Request } from 'express';
9 | import { ApiKeyService } from './api-key.service';
10 |
11 | @Injectable()
12 | export class APIKeyGuard implements CanActivate {
13 | private readonly logger = new Logger(APIKeyGuard.name);
14 |
15 | constructor(private apiKeyService: ApiKeyService) {}
16 |
17 | async canActivate(context: ExecutionContext): Promise {
18 | const request = context.switchToHttp().getRequest();
19 | const apiKey = request.headers['x-api-key'] as string;
20 |
21 | if (!apiKey) {
22 | this.logger.warn('API Key missing from headers.');
23 | throw new UnauthorizedException('API Key missing');
24 | }
25 |
26 | const isValid = this.apiKeyService.validateApiKey(apiKey);
27 |
28 | if (!isValid) {
29 | this.logger.warn(`Invalid API Key: ${apiKey}`);
30 | throw new UnauthorizedException('Invalid API Key');
31 | }
32 |
33 | return true;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/frontend/store/game-progress/game-progress-store.js:
--------------------------------------------------------------------------------
1 | import create from 'zustand';
2 | import { persist } from 'zustand/middleware';
3 |
4 | const useGameStore = create(
5 | persist(
6 | (set) => ({
7 | completedPuzzles: [],
8 | score: 0,
9 | achievements: [],
10 | addCompletedPuzzle: (puzzleId) =>
11 | set((state) => ({
12 | completedPuzzles: [...state.completedPuzzles, puzzleId],
13 | })),
14 | incrementScore: (points) =>
15 | set((state) => ({
16 | score: state.score + points,
17 | })),
18 | addAchievement: (achievement) =>
19 | set((state) => ({
20 | achievements: [...state.achievements, achievement],
21 | })),
22 | resetProgress: () =>
23 | set({
24 | completedPuzzles: [],
25 | score: 0,
26 | achievements: [],
27 | }),
28 | }),
29 | {
30 | name: 'game-storage', // unique name for storage
31 | getStorage: () => localStorage, // (optional) by default, 'localStorage' is used
32 | }
33 | )
34 | );
35 |
36 | export default useGameStore;
37 |
--------------------------------------------------------------------------------
/backend/src/achievements/entities/achievement.entity.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Entity,
3 | PrimaryGeneratedColumn,
4 | Column,
5 | CreateDateColumn,
6 | } from 'typeorm';
7 |
8 | export enum RuleType {
9 | PUZZLE_COMPLETION_TIME = 'puzzle_completion_time',
10 | LOGIN_STREAK = 'login_streak',
11 | TOTAL_PUZZLES_COMPLETED = 'total_puzzles_completed',
12 | FIRST_PUZZLE = 'first_puzzle',
13 | DAILY_LOGIN = 'daily_login',
14 | }
15 |
16 | @Entity('achievements')
17 | export class Achievement {
18 | @PrimaryGeneratedColumn('uuid')
19 | id: string;
20 |
21 | @Column({ type: 'varchar', length: 255 })
22 | title: string;
23 |
24 | @Column({ type: 'text' })
25 | description: string;
26 |
27 | @Column({ type: 'varchar', length: 500, name: 'icon_url' })
28 | iconUrl: string;
29 |
30 | @Column({
31 | type: 'enum',
32 | enum: RuleType,
33 | name: 'rule_type',
34 | })
35 | ruleType: RuleType;
36 |
37 | @Column({ type: 'json', name: 'rule_value' })
38 | ruleValue: any; // Flexible JSON field for different rule configurations
39 |
40 | @CreateDateColumn({ name: 'created_at' })
41 | createdAt: Date;
42 | }
43 |
--------------------------------------------------------------------------------
/backend/src/gameMechanics/entities/puzzle-submission.entity.ts:
--------------------------------------------------------------------------------
1 | import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, ManyToOne, JoinColumn } from "typeorm"
2 | import { User } from "./user.entity"
3 | import { Challenge } from "./challenge.entity"
4 |
5 | @Entity("puzzle_submissions")
6 | export class PuzzleSubmission {
7 | @PrimaryGeneratedColumn("uuid")
8 | id: string
9 |
10 | @Column("uuid")
11 | userId: string
12 |
13 | @Column("uuid")
14 | challengeId: string
15 |
16 | @Column("text")
17 | submittedAnswer: string
18 |
19 | @Column({ type: "boolean" })
20 | isCorrect: boolean
21 |
22 | @Column({ type: "int", nullable: true })
23 | timeTaken: number // in seconds
24 |
25 | @Column({ type: "inet", nullable: true })
26 | ipAddress: string
27 |
28 | @CreateDateColumn()
29 | createdAt: Date
30 |
31 | @ManyToOne(
32 | () => User,
33 | (user) => user.submissions,
34 | )
35 | @JoinColumn({ name: "userId" })
36 | user: User
37 |
38 | @ManyToOne(
39 | () => Challenge,
40 | (challenge) => challenge.submissions,
41 | )
42 | @JoinColumn({ name: "challengeId" })
43 | challenge: Challenge
44 | }
45 |
--------------------------------------------------------------------------------
/backend/src/activity/dto/filter-activity.dto.ts:
--------------------------------------------------------------------------------
1 | import { ApiPropertyOptional } from '@nestjs/swagger';
2 | import { IsEnum, IsOptional, IsDateString } from 'class-validator';
3 | import { ActivityType } from '../entities/activity.entity';
4 | import { Type } from 'class-transformer';
5 |
6 | export class FilterActivityDto {
7 | @ApiPropertyOptional({ enum: ActivityType, description: 'Filter by activity type' })
8 | @IsOptional()
9 | @IsEnum(ActivityType)
10 | type?: ActivityType;
11 |
12 | @ApiPropertyOptional({ type: String, format: 'date-time', description: 'Filter from date (ISO string)' })
13 | @IsOptional()
14 | @IsDateString()
15 | from?: string;
16 |
17 | @ApiPropertyOptional({ type: String, format: 'date-time', description: 'Filter to date (ISO string)' })
18 | @IsOptional()
19 | @IsDateString()
20 | to?: string;
21 |
22 | @ApiPropertyOptional({ type: Number, example: 10, description: 'Results per page' })
23 | @IsOptional()
24 | @Type(() => Number)
25 | limit?: number;
26 |
27 | @ApiPropertyOptional({ type: Number, example: 1, description: 'Page number' })
28 | @IsOptional()
29 | @Type(() => Number)
30 | page?: number;
31 | }
--------------------------------------------------------------------------------
/backend/src/gameMechanics/entities/challenge-completion.entity.ts:
--------------------------------------------------------------------------------
1 | import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, ManyToOne, JoinColumn } from "typeorm"
2 | import { User } from "./user.entity"
3 | import { Challenge } from "./challenge.entity"
4 |
5 | @Entity("challenge_completions")
6 | export class ChallengeCompletion {
7 | @PrimaryGeneratedColumn("uuid")
8 | id: string
9 |
10 | @Column("uuid")
11 | userId: string
12 |
13 | @Column("uuid")
14 | challengeId: string
15 |
16 | @Column({ type: "int" })
17 | pointsEarned: number
18 |
19 | @Column({ type: "int" })
20 | attemptsUsed: number
21 |
22 | @Column({ type: "int" })
23 | hintsUsed: number
24 |
25 | @Column({ type: "int", nullable: true })
26 | totalTimeTaken: number // in seconds
27 |
28 | @CreateDateColumn()
29 | completedAt: Date
30 |
31 | @ManyToOne(
32 | () => User,
33 | (user) => user.completions,
34 | )
35 | @JoinColumn({ name: "userId" })
36 | user: User
37 |
38 | @ManyToOne(
39 | () => Challenge,
40 | (challenge) => challenge.completions,
41 | )
42 | @JoinColumn({ name: "challengeId" })
43 | challenge: Challenge
44 | }
45 |
--------------------------------------------------------------------------------
/backend/src/multiplayer-queue/dto/queue-stats.dto.ts:
--------------------------------------------------------------------------------
1 | import { ApiProperty } from "@nestjs/swagger"
2 |
3 | export class QueueStatsDto {
4 | @ApiProperty({
5 | description: "Total players currently in queue",
6 | example: 15,
7 | })
8 | totalInQueue: number
9 |
10 | @ApiProperty({
11 | description: "Players waiting by skill level",
12 | example: {
13 | beginner: 5,
14 | intermediate: 7,
15 | advanced: 2,
16 | expert: 1,
17 | },
18 | })
19 | bySkillLevel: Record
20 |
21 | @ApiProperty({
22 | description: "Players waiting by game mode",
23 | example: {
24 | classic: 12,
25 | blitz: 3,
26 | },
27 | })
28 | byGameMode: Record
29 |
30 | @ApiProperty({
31 | description: "Average wait time in seconds",
32 | example: 45.5,
33 | })
34 | averageWaitTime: number
35 |
36 | @ApiProperty({
37 | description: "Longest wait time in seconds",
38 | example: 120,
39 | })
40 | longestWaitTime: number
41 |
42 | @ApiProperty({
43 | description: "Total matches created today",
44 | example: 87,
45 | })
46 | matchesToday: number
47 | }
48 |
--------------------------------------------------------------------------------
/backend/src/achievements/dto/player-achievements.dto.ts:
--------------------------------------------------------------------------------
1 | import { ApiProperty } from '@nestjs/swagger';
2 |
3 | export class PlayerAchievementDto {
4 | @ApiProperty({
5 | description: 'Unique identifier for the player achievement record',
6 | example: '123e4567-e89b-12d3-a456-426614174000',
7 | })
8 | id: string;
9 |
10 | @ApiProperty({
11 | description: 'Unique identifier for the achievement',
12 | example: '123e4567-e89b-12d3-a456-426614174001',
13 | })
14 | achievementId: string;
15 |
16 | @ApiProperty({
17 | description: 'Title of the achievement',
18 | example: 'Speed Demon',
19 | })
20 | title: string;
21 |
22 | @ApiProperty({
23 | description: 'Description of the achievement',
24 | example: 'Complete a puzzle in under 30 seconds',
25 | })
26 | description: string;
27 |
28 | @ApiProperty({
29 | description: 'URL to the achievement icon',
30 | example: 'https://example.com/icons/speed-demon.png',
31 | })
32 | iconUrl: string;
33 |
34 | @ApiProperty({
35 | description: 'Date and time when the achievement was earned',
36 | example: '2024-01-15T10:30:00Z',
37 | })
38 | earnedAt: Date;
39 | }
40 |
--------------------------------------------------------------------------------
/backend/src/admin/admin.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { TypeOrmModule } from '@nestjs/typeorm';
3 | import { JwtModule } from '@nestjs/jwt';
4 | import { Admin } from './admin.entity';
5 | import { AdminService } from './admin.service';
6 | import { AdminController } from './admin.controller';
7 | import { JwtStrategy } from './strategies/jwt.strategy';
8 | import { LocalStrategy } from './strategies/local.strategy';
9 | import { RolesGuard } from './guards/roles.guard';
10 | import { JwtAuthGuard } from './guards/jwt-auth.guard';
11 | import { APP_GUARD } from '@nestjs/core';
12 | import { AdminRole } from './admin-role.enum';
13 |
14 | @Module({
15 | imports: [
16 | TypeOrmModule.forFeature([Admin]),
17 | JwtModule.register({
18 | secret: process.env.JWT_SECRET || 'supersecret',
19 | signOptions: { expiresIn: '1d' },
20 | }),
21 | ],
22 | controllers: [AdminController],
23 | providers: [
24 | AdminService,
25 | JwtStrategy,
26 | LocalStrategy,
27 | { provide: APP_GUARD, useClass: JwtAuthGuard },
28 | { provide: APP_GUARD, useClass: RolesGuard },
29 | ],
30 | })
31 | export class AdminModule {}
32 |
--------------------------------------------------------------------------------
/backend/src/maintenance-mode/guards/admin.guard.ts:
--------------------------------------------------------------------------------
1 | import { Injectable, type CanActivate, type ExecutionContext, ForbiddenException } from "@nestjs/common"
2 | import type { Request } from "express"
3 |
4 | @Injectable()
5 | export class AdminGuard implements CanActivate {
6 | canActivate(context: ExecutionContext): boolean {
7 | const request = context.switchToHttp().getRequest()
8 |
9 | // Extract admin status from request
10 | // This is a simplified implementation - adjust based on your auth system
11 | const isAdmin = this.extractAdminStatusFromRequest(request)
12 |
13 | if (!isAdmin) {
14 | throw new ForbiddenException("Admin access required")
15 | }
16 |
17 | return true
18 | }
19 |
20 | private extractAdminStatusFromRequest(request: Request): boolean {
21 | // In a real implementation, you'd decode JWT token and check roles
22 | // For now, we'll check for a custom header (for testing purposes)
23 | const adminHeader = request.headers["x-admin"] as string
24 | const userRole = request.headers["x-user-role"] as string
25 |
26 | return adminHeader === "true" || userRole === "admin"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/backend/src/audit-log/audit-log.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@nestjs/common';
2 | import { InjectRepository } from '@nestjs/typeorm';
3 | import { Repository, Like, Between } from 'typeorm';
4 | import { AuditLog } from './entities/audit-log.entity';
5 |
6 | @Injectable()
7 | export class AuditLogService {
8 | constructor(
9 | @InjectRepository(AuditLog)
10 | private readonly auditRepo: Repository,
11 | ) {}
12 |
13 | async createLog(userId: string, action: string, meta?: Record) {
14 | const log = this.auditRepo.create({ userId, action, meta });
15 | return this.auditRepo.save(log);
16 | }
17 |
18 | async findAll(filters: {
19 | userId?: string;
20 | action?: string;
21 | startDate?: Date;
22 | endDate?: Date;
23 | }) {
24 | const where: any = {};
25 | if (filters.userId) where.userId = filters.userId;
26 | if (filters.action) where.action = Like(`%${filters.action}%`);
27 | if (filters.startDate && filters.endDate)
28 | where.timestamp = Between(filters.startDate, filters.endDate);
29 |
30 | return this.auditRepo.find({ where, order: { timestamp: 'DESC' } });
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/backend/src/token-verification/interceptors/token-header.interceptor.ts:
--------------------------------------------------------------------------------
1 | import { Injectable, type NestInterceptor, type ExecutionContext, type CallHandler } from "@nestjs/common"
2 | import type { Observable } from "rxjs"
3 | import { map } from "rxjs/operators"
4 |
5 | @Injectable()
6 | export class TokenHeaderInterceptor implements NestInterceptor {
7 | intercept(context: ExecutionContext, next: CallHandler): Observable {
8 | const response = context.switchToHttp().getResponse()
9 | const request = context.switchToHttp().getRequest()
10 |
11 | return next.handle().pipe(
12 | map((data) => {
13 | // Add token-related headers to response
14 | if (request.tokenExpiresAt) {
15 | response.setHeader("X-Token-Expires-At", request.tokenExpiresAt.toISOString())
16 | }
17 |
18 | if (request.tokenPayload?.sub) {
19 | response.setHeader("X-Token-Subject", request.tokenPayload.sub)
20 | }
21 |
22 | if (request.walletPayload?.address) {
23 | response.setHeader("X-Wallet-Address", request.walletPayload.address)
24 | }
25 |
26 | return data
27 | }),
28 | )
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/backend/src/puzzle-access-log/puzzle-access-log.controller.ts:
--------------------------------------------------------------------------------
1 | import { Controller, Post, Body, Get, Param, Query, ParseIntPipe, DefaultValuePipe, ValidationPipe } from '@nestjs/common';
2 | import { PuzzleAccessLogService } from './puzzle-access-log.service';
3 | import { LogAccessDto } from './dto/log-access.dto';
4 |
5 | @Controller('puzzle-access')
6 | export class PuzzleAccessLogController {
7 | constructor(private readonly accessLogService: PuzzleAccessLogService) {}
8 |
9 | @Post('log')
10 | logAccess(@Body(new ValidationPipe()) dto: LogAccessDto) {
11 | return this.accessLogService.logAccess(dto);
12 | }
13 |
14 | @Get('analytics/most-accessed')
15 | getMostAccessedPuzzles() {
16 | return this.accessLogService.getMostAccessedPuzzles();
17 | }
18 |
19 | @Get('analytics/unique-users/:puzzleId')
20 | getUniqueUsersPerPuzzle(@Param('puzzleId') puzzleId: string) {
21 | return this.accessLogService.getUniqueUsersPerPuzzle(puzzleId);
22 | }
23 |
24 | @Get('analytics/trends')
25 | getTimeBasedTrends(
26 | @Query('days', new DefaultValuePipe(7), ParseIntPipe) days: number,
27 | ) {
28 | return this.accessLogService.getTimeBasedTrends(days);
29 | }
30 | }
--------------------------------------------------------------------------------
/backend/src/milestone/dto/milestone-achievement.dto.ts:
--------------------------------------------------------------------------------
1 | import type { MilestoneCategory, MilestoneType } from "../entities/milestone-template.entity"
2 |
3 | export class MilestoneAchievementDto {
4 | id: string
5 | title: string
6 | description: string
7 | category: MilestoneCategory
8 | milestoneType: MilestoneType
9 | points: number
10 | badgeIcon?: string
11 | badgeColor?: string
12 | achievedAt: Date
13 | achievedValue?: number
14 | isViewed: boolean
15 | }
16 |
17 | export class UserMilestoneStatsDto {
18 | totalMilestones: number
19 | totalPoints: number
20 | milestonesByCategory: Record
21 | recentAchievements: MilestoneAchievementDto[]
22 | nextMilestones: NextMilestoneDto[]
23 | }
24 |
25 | export class NextMilestoneDto {
26 | id: string
27 | title: string
28 | description: string
29 | category: MilestoneCategory
30 | requiredCount?: number
31 | currentProgress: number
32 | progressPercentage: number
33 | pointsReward: number
34 | }
35 |
36 | export class ProgressUpdateDto {
37 | category: MilestoneCategory
38 | progressKey: string
39 | incrementValue?: number
40 | newValue?: number
41 | metadata?: any
42 | }
43 |
--------------------------------------------------------------------------------
/frontend/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | module.exports = {
3 | darkMode: ["class"],
4 | content: [
5 | "./pages/**/*.{js,ts,jsx,tsx,mdx}",
6 | "./components/**/*.{js,ts,jsx,tsx,mdx}",
7 | "./app/**/*.{js,ts,jsx,tsx,mdx}",
8 | ],
9 | theme: {
10 | extend: {
11 | colors: {
12 | background: 'var(--background)',
13 | foreground: 'var(--foreground)'
14 | },
15 | borderRadius: {
16 | lg: 'var(--radius)',
17 | md: 'calc(var(--radius) - 2px)',
18 | sm: 'calc(var(--radius) - 4px)'
19 | },
20 | keyframes: {
21 | 'accordion-down': {
22 | from: {
23 | height: '0'
24 | },
25 | to: {
26 | height: 'var(--radix-accordion-content-height)'
27 | }
28 | },
29 | 'accordion-up': {
30 | from: {
31 | height: 'var(--radix-accordion-content-height)'
32 | },
33 | to: {
34 | height: '0'
35 | }
36 | }
37 | },
38 | animation: {
39 | 'accordion-down': 'accordion-down 0.2s ease-out',
40 | 'accordion-up': 'accordion-up 0.2s ease-out'
41 | }
42 | }
43 | },
44 | plugins: [require("tailwindcss-animate")],
45 | };
46 |
--------------------------------------------------------------------------------
/backend/src/quiz/entities/quiz.entity.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Entity,
3 | PrimaryGeneratedColumn,
4 | Column,
5 | OneToMany,
6 | CreateDateColumn,
7 | UpdateDateColumn,
8 | } from 'typeorm';
9 | import { QuizQuestion } from './quiz-question.entity';
10 |
11 | @Entity('quizzes')
12 | export class Quiz {
13 | @PrimaryGeneratedColumn('uuid')
14 | id: string;
15 |
16 | @Column({ length: 255 })
17 | title: string;
18 |
19 | @Column({ type: 'text', nullable: true })
20 | description: string;
21 |
22 | @Column({ length: 100, nullable: true })
23 | topic: string;
24 |
25 | @Column({ type: 'int', default: 60 })
26 | timeLimit: number; // in minutes
27 |
28 | @Column({ type: 'int', default: 0 })
29 | passingScore: number; // percentage
30 |
31 | @Column({ default: true })
32 | isActive: boolean;
33 |
34 | @Column({ default: false })
35 | randomizeQuestions: boolean;
36 |
37 | @Column({ default: false })
38 | randomizeOptions: boolean;
39 |
40 | @OneToMany(() => QuizQuestion, (question) => question.quiz, { cascade: true })
41 | questions: QuizQuestion[];
42 |
43 | @CreateDateColumn()
44 | createdAt: Date;
45 |
46 | @UpdateDateColumn()
47 | updatedAt: Date;
48 | }
49 |
--------------------------------------------------------------------------------