{"id":17115666,"url":"https://github.com/annikacodes/betterpoll","last_synced_at":"2026-03-04T03:01:30.718Z","repository":{"id":38329866,"uuid":"439464674","full_name":"AnnikaCodes/betterpoll","owner":"AnnikaCodes","description":"Online ranked-choice polls made quick and easy","archived":false,"fork":false,"pushed_at":"2023-02-04T01:54:59.000Z","size":1026,"stargazers_count":4,"open_issues_count":8,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-13T04:14:29.967Z","etag":null,"topics":["poll","polling","ranked-choice-voting"],"latest_commit_sha":null,"homepage":"https://betterpoll.cc","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/AnnikaCodes.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-12-17T21:28:59.000Z","updated_at":"2024-12-22T17:32:31.000Z","dependencies_parsed_at":"2023-02-18T13:00:44.616Z","dependency_job_id":null,"html_url":"https://github.com/AnnikaCodes/betterpoll","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/AnnikaCodes/betterpoll","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AnnikaCodes%2Fbetterpoll","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AnnikaCodes%2Fbetterpoll/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AnnikaCodes%2Fbetterpoll/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AnnikaCodes%2Fbetterpoll/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AnnikaCodes","download_url":"https://codeload.github.com/AnnikaCodes/betterpoll/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AnnikaCodes%2Fbetterpoll/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30070479,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-04T01:03:42.280Z","status":"online","status_checked_at":"2026-03-04T02:00:07.464Z","response_time":59,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["poll","polling","ranked-choice-voting"],"created_at":"2024-10-14T17:45:48.453Z","updated_at":"2026-03-04T03:01:30.705Z","avatar_url":"https://github.com/AnnikaCodes.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# betterpoll\n[![Backend CI](https://github.com/AnnikaCodes/betterpoll/actions/workflows/backend.yml/badge.svg)](https://github.com/AnnikaCodes/betterpoll/actions/workflows/backend.yml) [![Frontend CI](https://github.com/AnnikaCodes/betterpoll/actions/workflows/frontend.yml/badge.svg)](https://github.com/AnnikaCodes/betterpoll/actions/workflows/frontend.yml) [![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/AnnikaCodes/betterpoll/blob/main/LICENSE)\n\nBetterPoll is a work-in-progress website to allow users to quickly and easily create and vote in ranked-choice polls.\n\nPlease note: this project is very new. There are many TODOs littered around the code, and it is not even ready to be publicly deployed in a testing phase.\n\n## Backend\n### API\nBetterPoll's backend (whose source code is located in the `backend/` directory) exposes a REST-ish API:\n- `POST /poll/\u003cpollid\u003e/vote` with candidate choices to vote\n    - Provided data should be JSON of the form `{\"choices\":[]}`, where the `choices` key is an array of candidate strings\n    - Response will be `{\"success\": true}` or equivalent JSON if the vote succeeds, and `{\"success\": false, \"error\": \u003cerrorstring\u003e}` or equivalent if it fails (where `\u003cerrorstring\u003e` is a string explaining the error that occured)\n- `GET /poll/\u003cpollid\u003e` to get info about a poll\n    - In the event of an error, the response will be JSON of the form `{\"success\": false, \"error\": \u003cerrorstring\u003e}`, where `\u003cerrorstring\u003e` is a human-readable string describing the error that occurred.\n    - On success, the response will be JSON with the following properties:\n        - `success` (boolean): `true`.\n        - `name` (string): the name of the poll.\n        - `description` (string): the poll's description.\n        - `candidates` (array of strings): choices for which users can vote.\n        - `creationTime` (integer): UNIX timestamp at which the poll began (in seconds).\n        - `endingTime` (integer): UNIX timestamp at which the poll ends (in seconds).\n        - `numWinners` (integer): number of winners the poll has.\n        - `protection` (string or null): `\"ip\"` if votes by the same IP address are forbidden, and `null` otherwise.\n        - `numVotes` (integer): the number of votes cast so far.\n        - `ended` (boolean): `true` if the poll has ended, otherwise `false`.\n    - If the poll has ended, the following additional properties will be specified in the response JSON:\n        - `winners` (array of strings): the winner(s) of the poll. May be more/less than `numWinners` if multiple winners have the same rank in the overall tally.\n- `POST /create` to create a poll\n    - Provided data should be JSON, with the following **mandatory** properties:\n        - `name` (string): the name for the poll.\n        - `description` (string): a description of the poll.\n        - `candidates` (array of strings): choices for which users can vote. Should be between 2 and 1024 in length.\n        - `duration` (integer): the amount of time after which the poll will expire, in seconds. Must be positive.\n        - `numWinners` (integer): the number of winners that the poll can have. Must be greater than 0 and less than the number of candidates provided.\n    - The following properties are **optional**:\n        - `id` (string): a custom URL for the poll. Must be a string composed of letters A-Z (upper or lowercase), numbers 0-9, `_`, `.` and `-`, with at least 1 and at most 32 characters.\n        - `protection` (string): the protection method to use to prevent double voting. Currently, the only acceptable values are `ip` (prevents multiple votes from the same IP address) and `none` (allows all incoming votes). In the future, more protection methods may be implemented.\n    - Response on success is JSON of the form `{\"success\": true, \"id\": \u003cid\u003e}`, where `\u003cid\u003e` is the poll's ID. On error, the response will be JSON of the form `{\"success\": false, \"error\": \u003cerrorstring\u003e}`, where `\u003cerrorstring\u003e` is a human-readable string describing the error that occurred.\n- `GET /status` to get status information\n    - Returns JSON with the following properties:\n        - `success`: `true`\n        - `total`: the total number of polls in the database, or `null` if the database is inaccessible\n        - `active`: the number of polls currently accepting votes, or `null` if the database is inaccessible\n\n### Database\nBetterPoll currently uses PostgreSQL for its database, although it's possible that alternative databases will be added in the future.\n\nDatabase tests can be disabled with the `no-db-test` feature.\n\nThere is a database schema at `backend/schema.sql`; you'll need to run this to set up the requisite tables before starting the backend server.\n\n## Frontend\nBetterPoll's frontend is written in Vue and located in the `frontend/` directory.\n\nI plan to generate a static site from this that can be served through something like GitHub Pages.\n\n## Configuration\nConfigure the databases in `Rocket.toml`; an [example](https://github.com/AnnikaCodes/betterpoll/blob/main/backend/Rocket.example.toml) is provided.\n\nYou'll also need to specify `ALLOWED_ORIGINS` as an environment variable (or in a `.env` file); it is a regular expression specifying allowed origins for CORS.\n\nYou may optionally specify the `API_URL` environment variable (to use an alternate backend) or the `DOMAIN` environment variable (which specifies the domain used in the UI display for custom URLs). However, this is optional; sane defaults are provided.\n\n## Credits\nMost of the libraries used can be found in the `Cargo.toml` and `package.json` files, but there is one I want to mention specifically:\n- [@phayes](https://github.com/phayes/)'s [`tallystick`](https://crates.io/crate/tallystick) library is used to provide implementations of the voting algorithms. Currently, only the Schulze method is supported, but it's definitely possible to add more in the future.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fannikacodes%2Fbetterpoll","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fannikacodes%2Fbetterpoll","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fannikacodes%2Fbetterpoll/lists"}