diff --git a/.env.example b/.env.example index 4905f4a..16ef135 100644 --- a/.env.example +++ b/.env.example @@ -2,4 +2,5 @@ DATABASE_URL= WHITELISTED_ORIGINS= TUNNEL_DOMAINS= JWT_SECRET= -VERIFICATION_LEVEL="device" # "orb" or "device", default is "device" \ No newline at end of file +ENABLE_VOTE_NORMALIZATION= +VERIFICATION_LEVEL="device" # "orb" or "device", default is "device" diff --git a/README.md b/README.md index c35976c..96e5655 100644 --- a/README.md +++ b/README.md @@ -1,99 +1,339 @@ -
- -[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456 -[circleci-url]: https://circleci.com/gh/nestjs/nest - -A progressive Node.js framework for building efficient and scalable server-side applications.
- - - -## Description - -[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository. - -## Project setup +# Worldview Backend + +## 1. Project Overview + +### Purpose +Worldview is a quadratic voting platform based on World mini-apps that allows users to create, manage, and participate in polls. The platform enables community-driven decision making through a weighted voting system, giving users the ability to express preference strength across multiple options. + +### Key Features +- Poll creation with draft capability +- Quadratic voting system for more representative outcomes +- User authentication using World ID +- Poll searching, filtering, and sorting +- Vote management for users +- Anonymous voting option + +### Live Links +- Production: https://backend.worldview.fyi +- Staging: https://backend.staging.worldview.fyi + +## 2. Architecture Overview + +### Tech Stack +- **Framework**: NestJS (Node.js) +- **Database**: PostgreSQL with Prisma ORM +- **Authentication**: JWT, World ID integration +- **Voting System**: Quadratic voting with normalization options +- **Docker**: Container-based deployment + +### System Architecture + +``` +┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ +│ │ │ │ │ │ +│ Frontend App │<────>│ NestJS API │<────>│ PostgreSQL DB │ +│ │ │ │ │ │ +└─────────────────┘ └─────────────────┘ └─────────────────┘ + │ + │ + ┌───────▼───────┐ + │ │ + │ World ID │ + │ Integration │ + │ │ + └───────────────┘ +``` + +### Data Flow +1. Users authenticate via World ID +2. Authenticated users can create polls (draft or published) +3. Users can vote on active polls using quadratic voting +4. The system normalizes and calculates voting weights (based on feature flag) +5. Poll results are available for viewing based on permissions + +## 3. Getting Started + +### Prerequisites +- Node.js (v18+) +- PostgreSQL (v14+) +- Yarn package manager + +### Installation Steps ```bash +# Install dependencies $ yarn install + +# Generate Prisma client +$ npx prisma generate + +# Run database migrations +$ npx prisma migrate dev +``` + +### Configuration +Create a `.env` file in the root directory with the following variables: +``` +DATABASE_URL="postgresql://user:password@localhost:5432/worldview" +JWT_SECRET="your-secret-key" +WHITELISTED_ORIGINS="localhost:3000,yourdomain.com" +ENABLE_VOTE_NORMALIZATION=true +TUNNEL_DOMAINS="localtunnel.me,ngrok.io" ``` -## Compile and run the project +## 4. Usage Instructions +### Running the Application ```bash -# development -$ yarn run start - -# watch mode +# Development mode $ yarn run start:dev -# production mode +# Production mode +$ yarn run build $ yarn run start:prod ``` -## Run tests - +### Database Migrations ```bash -# unit tests -$ yarn run test +# Generate a new migration +$ yarn migration:generate + +# Apply migrations (production) +$ yarn migration:prod +``` + +## 5. API Endpoints -# e2e tests -$ yarn run test:e2e +### Authentication +- `GET /auth/nonce` - Get a nonce for World ID authentication +- `POST /auth/verifyWorldId` - Verify World ID and authenticate user -# test coverage -$ yarn run test:cov +### Polls +- `POST /poll` - Create a new published poll +- `PATCH /poll/draft` - Create or update a draft poll +- `GET /poll/draft` - Get user's draft poll +- `GET /poll` - Get polls with filtering and pagination + - Query params: page, limit, isActive, userVoted, userCreated, search, sortBy, sortOrder +- `GET /poll/:id` - Get detailed information about a specific poll +- `DELETE /poll/:id` - Delete a poll (if owner) + +### Users +- `GET /user/getUserData` - Get user profile data +- `GET /user/getUserActivities` - Get user activities +- `GET /user/getUserVotes` - Get user's voting history +- `POST /user/setVote` - Cast a vote on a poll +- `POST /user/editVote` - Edit an existing vote +- `POST /user/createUser` - Create a new user + +## 6. Database Schema + +### Main Entities + +#### User +```prisma +model User { + id Int @id @default(autoincrement()) + worldID String @unique + name String? + profilePicture String? + pollsCreatedCount Int @default(0) + pollsParticipatedCount Int @default(0) + createdPolls Poll[] @relation("PollAuthor") + actions UserAction[] + votes Vote[] +} +``` + +#### Poll +```prisma +model Poll { + pollId Int @id @default(autoincrement()) + authorUserId Int + title String? + description String? + options String[] + creationDate DateTime @default(now()) + startDate DateTime? + endDate DateTime? + tags String[] + isAnonymous Boolean @default(false) + participantCount Int @default(0) + voteResults Json + searchVector Unsupported("tsvector")? + status PollStatus @default(PUBLISHED) + author User @relation("PollAuthor", fields: [authorUserId], references: [id]) + userAction UserAction[] + votes Vote[] +} ``` -## Deployment +#### Vote +```prisma +model Vote { + voteID String @id @default(uuid()) + userId Int + pollId Int + votingPower Int + weightDistribution Json + proof String + quadraticWeights Json? + normalizedWeightDistribution Json? + normalizedQuadraticWeights Json? + poll Poll @relation(fields: [pollId], references: [pollId], onDelete: Cascade) + user User @relation(fields: [userId], references: [id]) +} +``` -When you're ready to deploy your NestJS application to production, there are some key steps you can take to ensure it runs as efficiently as possible. Check out the [deployment documentation](https://docs.nestjs.com/deployment) for more information. +#### UserAction +```prisma +model UserAction { + id Int @id @default(autoincrement()) + userId Int + pollId Int + type ActionType + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) @updatedAt + poll Poll @relation(fields: [pollId], references: [pollId], onDelete: Cascade) + user User @relation(fields: [userId], references: [id]) +} +``` -If you are looking for a cloud-based platform to deploy your NestJS application, check out [Mau](https://mau.nestjs.com), our official platform for deploying NestJS applications on AWS. Mau makes deployment straightforward and fast, requiring just a few simple steps: +## 7. File Structure -```bash -$ yarn install -g mau -$ mau deploy +``` +worldview-be +│ +├── prisma/ # Database schema and migrations +│ ├── schema.prisma # Database model definitions +│ └── migrations/ # Database migration files +│ +├── src/ +│ ├── auth/ # Authentication module +│ │ ├── auth.controller.ts +│ │ ├── auth.service.ts +│ │ ├── jwt.service.ts +│ │ └── jwt-auth.guard.ts +│ │ +│ ├── poll/ # Poll management module +│ │ ├── poll.controller.ts +│ │ ├── poll.service.ts +│ │ ├── Poll.dto.ts +│ │ └── poll.module.ts +│ │ +│ ├── user/ # User management module +│ │ ├── user.controller.ts +│ │ ├── user.service.ts +│ │ ├── user.dto.ts +│ │ └── user.module.ts +│ │ +│ ├── common/ # Shared utilities and exceptions +│ │ ├── exceptions.ts +│ │ ├── http-exception.filter.ts +│ │ └── validators.ts +│ │ +│ ├── database/ # Database connection module +│ │ └── database.service.ts +│ │ +│ ├── app.module.ts # Main application module +│ ├── app.controller.ts # Main application controller +│ ├── app.service.ts # Main application service +│ └── main.ts # Application entry point +│ +├── test/ # Test files +│ +├── .env # Environment variables (not in repo) +├── docker-compose.yml # Docker Compose configuration +└── package.json # Project dependencies and scripts ``` -With Mau, you can deploy your application in just a few clicks, allowing you to focus on building features rather than managing infrastructure. +## 8. Key Features Implementation -## Resources +### Draft Poll Management +The system allows users to save incomplete polls as drafts before publishing: +- Only one draft poll per user +- Draft polls are not visible to other users +- Fields are optional in draft mode +- Simple conversion from draft to published -Check out a few resources that may come in handy when working with NestJS: +Implementation details: +- The Poll entity includes a `status` field with DRAFT/PUBLISHED values +- `patchDraftPoll` endpoint handles creating and updating drafts +- Transaction-based approach ensures only one draft exists per user +- Poll queries include status filters to separate draft and published polls -- Visit the [NestJS Documentation](https://docs.nestjs.com) to learn more about the framework. -- For questions and support, please visit our [Discord channel](https://discord.gg/G7Qnnhy). -- To dive deeper and get more hands-on experience, check out our official video [courses](https://courses.nestjs.com/). -- Deploy your application to AWS with the help of [NestJS Mau](https://mau.nestjs.com) in just a few clicks. -- Visualize your application graph and interact with the NestJS application in real-time using [NestJS Devtools](https://devtools.nestjs.com). -- Need help with your project (part-time to full-time)? Check out our official [enterprise support](https://enterprise.nestjs.com). -- To stay in the loop and get updates, follow us on [X](https://x.com/nestframework) and [LinkedIn](https://linkedin.com/company/nestjs). -- Looking for a job, or have a job to offer? Check out our official [Jobs board](https://jobs.nestjs.com). +### Quadratic Voting System +The platform uses quadratic voting for more democratic decision-making: +``` +1. Users distribute voting power across options +2. Final weight = square root of allocated points +3. Optional normalization for fair comparison +``` -## Support +Implementation details: +- Vote entity stores both raw weight distribution and quadratic weights +- Optional normalization can be enabled via environment variable +- PostgreSQL functions calculate quadratic weights and normalization +- Results are aggregated with proper weight calculations + +### Authentication Flow +1. User requests a nonce from the server +2. User authenticates with World ID +3. Server verifies World ID proof and issues JWT +4. JWT token is used for subsequent authenticated requests + +### Poll Filtering and Search +The API provides comprehensive filtering options: +- Active/inactive status filter based on poll date +- User participation filters (created/voted) +- Full-text search on title and description using PostgreSQL tsvector +- Custom sorting options (end date, participant count, creation date) + +### Vote Management +Users can: +- Cast votes with custom weight distribution +- Edit votes during active poll period +- View their voting history +- See vote calculations with quadratic weights + +## 9. Development Guidelines + +### Code Structure +- **Controllers**: Handle HTTP requests and responses +- **Services**: Implement business logic and database operations +- **DTOs**: Define data transfer objects for request/response validation +- **Guards**: Handle authentication and authorization +- **Exceptions**: Custom error handling and messages + +### Contribution Process +1. Fork the repository +2. Create a feature branch +3. Submit a pull request with detailed description +4. Pass automated tests +5. Complete code review + +## 10. Deployment + +### Docker Deployment +```bash +# Build the Docker image +docker-compose build + +# Run with Docker Compose +docker-compose up -d +``` -Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support). +### Environment-Specific Configuration +- Development: Uses local database, allows tunnel domains +- Production: Uses secure connections, restricted CORS -## Stay in touch +## 11. Troubleshooting -- Author - [Kamil Myśliwiec](https://twitter.com/kammysliwiec) -- Website - [https://nestjs.com](https://nestjs.com/) -- Twitter - [@nestframework](https://twitter.com/nestframework) +### Common Issues +- **Authentication failures**: Check World ID configuration +- **Database connection issues**: Verify DATABASE_URL +- **CORS errors**: Update WHITELISTED_ORIGINS +- **Vote calculation issues**: Check ENABLE_VOTE_NORMALIZATION setting -## License +### Logging +- Application logs are available in both development and production environments via Grafana (For access please reach out to devOps) -Nest is [MIT licensed](https://github.com/nestjs/nest/blob/master/LICENSE). diff --git a/prisma/migrations/20250503203644_add_poll_status/migration.sql b/prisma/migrations/20250503203644_add_poll_status/migration.sql new file mode 100644 index 0000000..c596701 --- /dev/null +++ b/prisma/migrations/20250503203644_add_poll_status/migration.sql @@ -0,0 +1,5 @@ +-- CreateEnum +CREATE TYPE "PollStatus" AS ENUM ('DRAFT', 'PUBLISHED'); + +-- AlterTable +ALTER TABLE "Poll" ADD COLUMN "status" "PollStatus" NOT NULL DEFAULT 'PUBLISHED'; diff --git a/prisma/migrations/20250503214151_make_poll_fields_optional/migration.sql b/prisma/migrations/20250503214151_make_poll_fields_optional/migration.sql new file mode 100644 index 0000000..4e92a40 --- /dev/null +++ b/prisma/migrations/20250503214151_make_poll_fields_optional/migration.sql @@ -0,0 +1,4 @@ +-- AlterTable +ALTER TABLE "Poll" ALTER COLUMN "title" DROP NOT NULL, +ALTER COLUMN "startDate" DROP NOT NULL, +ALTER COLUMN "endDate" DROP NOT NULL; diff --git a/prisma/migrations/20250512113354_add_generated_normalized_votes/migration.sql b/prisma/migrations/20250512113354_add_generated_normalized_votes/migration.sql new file mode 100644 index 0000000..df56a59 --- /dev/null +++ b/prisma/migrations/20250512113354_add_generated_normalized_votes/migration.sql @@ -0,0 +1,109 @@ +-- First drop the existing trigger that might be using these functions +DROP TRIGGER IF EXISTS normalize_weights_trigger ON "Vote"; + +-- Drop existing functions for clean recreation +DROP FUNCTION IF EXISTS update_normalized_weights(); +DROP FUNCTION IF EXISTS json_normalize_weights(jsonb, integer); + +-- Recreate the normalize weights function with IMMUTABLE +CREATE OR REPLACE FUNCTION json_normalize_weights(weight_distribution JSONB, voting_power INTEGER) +RETURNS JSONB AS $$ +DECLARE + total NUMERIC := 0; + weight NUMERIC; + key TEXT; + normalized_weights JSONB := '{}'::JSONB; + normalized_total NUMERIC := 0; + diff INTEGER; + first_nonzero_key TEXT := NULL; +BEGIN + -- First check if input is a valid JSON object + IF weight_distribution IS NULL OR jsonb_typeof(weight_distribution) != 'object' THEN + -- Return empty object if input is not a valid object + RETURN '{}'::JSONB; + END IF; + + -- Calculate total weights + FOR key, weight IN SELECT * FROM jsonb_each_text(weight_distribution) + LOOP + total := total + weight::NUMERIC; + END LOOP; + + -- If total is 0 or already equals target, return original + IF total = 0 OR total = voting_power THEN + RETURN weight_distribution; + END IF; + + -- Normalize each weight + FOR key, weight IN SELECT * FROM jsonb_each_text(weight_distribution) + LOOP + -- Formula: normalizedValue = (weightValue / sumOfAllWeights) * voting_power + -- Round to integer to avoid fractional weights + normalized_weights := normalized_weights || + jsonb_build_object(key, ROUND((weight::NUMERIC / total) * voting_power)); + + -- Keep track of first non-zero value to adjust if necessary + IF first_nonzero_key IS NULL AND weight::NUMERIC > 0 THEN + first_nonzero_key := key; + END IF; + END LOOP; + + -- Check if rounding caused total to drift from target + SELECT SUM(value::NUMERIC) INTO normalized_total FROM jsonb_each_text(normalized_weights); + + IF normalized_total != voting_power AND first_nonzero_key IS NOT NULL THEN + diff := voting_power - normalized_total; + normalized_weights := jsonb_set( + normalized_weights, + ARRAY[first_nonzero_key], + to_jsonb((normalized_weights->>first_nonzero_key)::NUMERIC + diff) + ); + END IF; + + RETURN normalized_weights; +END; +$$ LANGUAGE plpgsql IMMUTABLE; + +-- Make sure calculate_normalized_quadratic_weights is immutable +DROP FUNCTION IF EXISTS calculate_normalized_quadratic_weights(jsonb); + +CREATE OR REPLACE FUNCTION calculate_normalized_quadratic_weights(weights JSONB) +RETURNS JSONB AS $$ +DECLARE + weight NUMERIC; + key TEXT; + quadratic_weights JSONB := '{}'::JSONB; +BEGIN + -- Calculate quadratic weights for each value + FOR key, weight IN SELECT * FROM jsonb_each_text(weights) + LOOP + -- Formula: quadraticWeight = sqrt(weight) + quadratic_weights := quadratic_weights || + jsonb_build_object(key, ROUND(SQRT(weight::NUMERIC), 2)); + END LOOP; + + RETURN quadratic_weights; +END; +$$ LANGUAGE plpgsql IMMUTABLE; + +-- Only drop and recreate the columns we're interested in +ALTER TABLE "Vote" DROP COLUMN IF EXISTS "normalizedWeightDistribution"; +ALTER TABLE "Vote" DROP COLUMN IF EXISTS "normalizedQuadraticWeights"; + +-- Add generated columns +ALTER TABLE "Vote" ADD COLUMN "normalizedWeightDistribution" JSONB + GENERATED ALWAYS AS (json_normalize_weights("weightDistribution", "votingPower")) STORED; + +ALTER TABLE "Vote" ADD COLUMN "normalizedQuadraticWeights" JSONB + GENERATED ALWAYS AS (calculate_normalized_quadratic_weights(json_normalize_weights("weightDistribution", "votingPower"))) STORED; + +-- Comment explaining usage +COMMENT ON FUNCTION json_normalize_weights IS + 'Normalizes a JSON weight distribution so values sum to the specified voting power. + Usage: SELECT json_normalize_weights(''{"option1": 10, "option2": 15}'', 100) + Returns: {"option1": 40, "option2": 60}'; + +COMMENT ON FUNCTION calculate_normalized_quadratic_weights IS + 'Calculates quadratic weights from linear weights. + Usage: SELECT calculate_normalized_quadratic_weights(''{"option1": 40, "option2": 60}'') + Returns: {"option1": 6.32, "option2": 7.75}'; \ No newline at end of file diff --git a/prisma/migrations/20250514122507_add_created_at_to_user_schema/migration.sql b/prisma/migrations/20250514122507_add_created_at_to_user_schema/migration.sql new file mode 100644 index 0000000..eb3480e --- /dev/null +++ b/prisma/migrations/20250514122507_add_created_at_to_user_schema/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "User" ADD COLUMN "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 7f62bd2..6e8df3f 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -14,6 +14,7 @@ model User { profilePicture String? pollsCreatedCount Int @default(0) pollsParticipatedCount Int @default(0) + createdAt DateTime @default(now()) createdPolls Poll[] @relation("PollAuthor") actions UserAction[] votes Vote[] @@ -33,35 +34,43 @@ model UserAction { model Poll { pollId Int @id @default(autoincrement()) authorUserId Int - title String + title String? description String? options String[] creationDate DateTime @default(now()) - startDate DateTime - endDate DateTime + startDate DateTime? + endDate DateTime? tags String[] isAnonymous Boolean @default(false) participantCount Int @default(0) voteResults Json searchVector Unsupported("tsvector")? + status PollStatus @default(PUBLISHED) author User @relation("PollAuthor", fields: [authorUserId], references: [id]) userAction UserAction[] votes Vote[] } model Vote { - voteID String @id @default(uuid()) - userId Int - pollId Int - votingPower Int - weightDistribution Json - proof String - quadraticWeights Json? @default(dbgenerated("calculate_quadratic_weights(\"weightDistribution\")")) - poll Poll @relation(fields: [pollId], references: [pollId], onDelete: Cascade) - user User @relation(fields: [userId], references: [id]) + voteID String @id @default(uuid()) + userId Int + pollId Int + votingPower Int + weightDistribution Json + proof String + quadraticWeights Json? @default(dbgenerated("calculate_quadratic_weights(\"weightDistribution\")")) + normalizedWeightDistribution Json? @default(dbgenerated("json_normalize_weights(\"weightDistribution\", \"votingPower\")")) + normalizedQuadraticWeights Json? @default(dbgenerated("calculate_normalized_quadratic_weights(json_normalize_weights(\"weightDistribution\", \"votingPower\"))")) + poll Poll @relation(fields: [pollId], references: [pollId], onDelete: Cascade) + user User @relation(fields: [userId], references: [id]) } enum ActionType { CREATED VOTED } + +enum PollStatus { + DRAFT + PUBLISHED +} diff --git a/src/app.module.ts b/src/app.module.ts index 6fc528f..5765855 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -4,12 +4,13 @@ import { AppService } from './app.service'; import { DatabaseService } from './database/database.service'; import { DatabaseModule } from './database/database.module'; import { PollModule } from './poll/poll.module'; +import { VoteModule } from './vote/vote.module'; import { AuthModule } from './auth/auth.module'; import { AuthService } from './auth/auth.service'; import { UserModule } from './user/user.module'; @Module({ - imports: [DatabaseModule, PollModule, UserModule, AuthModule], + imports: [DatabaseModule, PollModule, VoteModule, UserModule, AuthModule], controllers: [AppController], providers: [AppService, DatabaseService, AuthService], }) diff --git a/src/common/common.dto.ts b/src/common/common.dto.ts new file mode 100644 index 0000000..502b32b --- /dev/null +++ b/src/common/common.dto.ts @@ -0,0 +1,15 @@ +import { IsDateString, IsOptional, IsString, Matches } from 'class-validator'; + +export class GetCountDto { + @IsOptional() + @IsString() + @Matches(/^\d{4}-\d{2}-\d{2}$/) + @IsDateString({ strict: true }) + from?: string; + + @IsOptional() + @IsString() + @Matches(/^\d{4}-\d{2}-\d{2}$/) + @IsDateString({ strict: true }) + to?: string; +} diff --git a/src/poll/Poll.dto.ts b/src/poll/Poll.dto.ts index 4e55d62..fc42e68 100644 --- a/src/poll/Poll.dto.ts +++ b/src/poll/Poll.dto.ts @@ -9,7 +9,9 @@ import { IsOptional, IsString, Min, + Validate, } from 'class-validator'; +import { IsPositiveInteger } from '../common/validators'; export class CreatePollDto { @IsString() @@ -43,6 +45,43 @@ export class CreatePollDto { isAnonymous?: boolean; } +export class DraftPollDto { + @IsOptional() + @IsString() + title?: string; + + @IsOptional() + @IsString() + description?: string; + + @IsOptional() + @IsArray() + @IsString({ each: true }) + options?: string[]; + + @IsOptional() + @IsDateString() + startDate?: string; + + @IsOptional() + @IsDateString() + endDate?: string; + + @IsOptional() + @IsArray() + @IsString({ each: true }) + tags?: string[]; + + @IsOptional() + @IsBoolean() + isAnonymous?: boolean; + + @IsOptional() + @Validate(IsPositiveInteger) + @Transform(({ value }) => (value ? parseInt(value, 10) : undefined)) + pollId?: number; +} + export class GetPollsDto { @IsString() @IsOptional() @@ -83,3 +122,10 @@ export class GetPollsDto { @IsEnum(['asc', 'desc']) sortOrder?: 'asc' | 'desc'; } + +export class GetPollVotesDto { + @IsInt() + @Type(() => Number) + @IsNotEmpty() + pollId: number; +} diff --git a/src/poll/poll.controller.ts b/src/poll/poll.controller.ts index e514297..a8572d4 100644 --- a/src/poll/poll.controller.ts +++ b/src/poll/poll.controller.ts @@ -4,12 +4,15 @@ import { Delete, Get, Param, + Patch, Post, Query, } from '@nestjs/common'; -import { CreatePollDto, GetPollsDto } from './Poll.dto'; -import { PollService } from './poll.service'; import { User } from 'src/auth/user.decorator'; +import { CreatePollDto, DraftPollDto, GetPollsDto } from './Poll.dto'; +import { PollService } from './poll.service'; +import { Public } from 'src/auth/jwt-auth.guard'; +import { GetCountDto } from '../common/common.dto'; @Controller('poll') export class PollController { @@ -23,6 +26,19 @@ export class PollController { return await this.pollService.createPoll(dto, worldID); } + @Patch('draft') + async patchDraftPoll( + @Body() dto: DraftPollDto, + @User('worldID') worldID: string, + ) { + return await this.pollService.patchDraftPoll(dto, worldID); + } + + @Get('draft') + async getDraftPoll(@User('worldID') worldID: string) { + return await this.pollService.getUserDraftPoll(worldID); + } + @Get() async getPolls( @Query() query: GetPollsDto, @@ -31,11 +47,22 @@ export class PollController { return await this.pollService.getPolls(query, worldID); } + @Get('count') + @Public() + async getPollsCount(@Query() query: GetCountDto): Promise