{"id":23257005,"url":"https://github.com/solvro/backend-topwr","last_synced_at":"2025-08-20T14:33:13.239Z","repository":{"id":262899272,"uuid":"888059562","full_name":"Solvro/backend-topwr","owner":"Solvro","description":null,"archived":false,"fork":false,"pushed_at":"2024-12-16T08:29:04.000Z","size":1340,"stargazers_count":17,"open_issues_count":16,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-12-16T09:20:51.367Z","etag":null,"topics":["adonis","api","backend","pwr","rest","student","students","wust"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Solvro.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-11-13T18:36:47.000Z","updated_at":"2024-12-14T10:37:34.000Z","dependencies_parsed_at":"2024-11-30T19:25:12.541Z","dependency_job_id":"acdb67ae-79b8-461d-a520-3edd3a93105a","html_url":"https://github.com/Solvro/backend-topwr","commit_stats":null,"previous_names":["solvro/backend-topwr"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Solvro%2Fbackend-topwr","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Solvro%2Fbackend-topwr/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Solvro%2Fbackend-topwr/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Solvro%2Fbackend-topwr/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Solvro","download_url":"https://codeload.github.com/Solvro/backend-topwr/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":230432940,"owners_count":18224995,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["adonis","api","backend","pwr","rest","student","students","wust"],"created_at":"2024-12-19T12:26:37.018Z","updated_at":"2025-08-20T14:33:13.220Z","avatar_url":"https://github.com/Solvro.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ToPWR Backend\n\n![Solvro banner](https://github.com/Solvro/backend-topwr-sks/blob/main/assets/solvro_dark.png#gh-dark-mode-only)\n![Solvro banner](https://github.com/Solvro/backend-topwr-sks/blob/main/assets/solvro_dark.png#gh-light-mode-only)\n\n## API Documentation\n\n### Disclaimer\n\nDocumentation and API are in WiP stage\n\nSome endpoints require authentication.\nTo authenticate, first generate a token using the `POST /api/v1/auth/login` endpoint.\nThen pass the received token via the `Authorization` header as a `Bearer` token. (`Authorization: Bearer \u003ctoken\u003e`)\n\n### Endpoints\n\n#### Custom controllers\n\nThe following controllers were implemented manually and contain custom endpoints that might function differently to autogenerated controllers.\n\n##### Files\n\n- **GET /api/v1/files/:key**\n  - Returns metadata for a given file\n  - Response: serialized `FileEntry` instnace\n- **POST /api/v1/files**\n  - **Requres authentication**\n  - Uploads a new file to the server\n  - Requires a multipart request body\n    - File must be uploaded in a field called `file`\n  - Response: `{ \"key\": \"\u003cfull key of the uploaded file\u003e\" }`\n\n##### About Us\n\n- **GET /api/v1/about_us**\n  - Returns \"about us\" data\n  - Response: `{ \"data\": { \"aboutUs\": AboutUsGeneral, \"solvroSocialLinks\": AboutUsGeneralLink[] } }`\n    - the AboutUsGeneral object has the `coverPhoto` relation automatically included\n\n##### Authentication\n\n- **POST /api/v1/auth/login**\n  - Requires a json request body: `{ \"email\": string, \"password\": string, \"rememberMe\"?: boolean }`\n    - technically you can also pass these with query parameters, but please don't\n  - Creates and returns a new token for the specified user, if the credentials are correct\n    - the token is valid for **30 days** if `rememberMe` = true, **1 day** otherwise.\n  - Response: `{ \"user\": User, \"token\": \"\u003ctoken string\u003e\" }`\n- **GET /api/v1/auth/me**\n  - **Requires authentication**\n  - Returns your user object\n  - Response: serialized `User` instance\n- **POST /api/v1/auth/logout**\n  - **Requires authentication**\n  - Invalidates the current token\n  - Response: `{ \"success\": true, \"message\": \"Logged out\" }`\n\n##### Newsfeed\n\n- **GET /api/v1/newsfeed/latest**\n  - Returns the first page of university news items\n  - Accepts optional query parameters:\n    - `completeOnly` - boolean, defaults to `true`, only includes fully scraped news items (with no missing fields)\n    - `lang` - enum: `pl`, `en`; defaults to `pl`, selects the newsfeed's language, languages contain different news items\n  - Response: `{ \"articles\": { \"url\": string, \"imageLink\": string, \"title\": string, \"previewText\": string, \"date\": string, \"categories\": string[] }[], \"updateTime\": string }`\n- **GET /api/v1/newsfeed/stats**\n  - Returns stats about the scraped news items\n    - includes count of articles scraped and the last update timestamp\n  - Response: `Partial\u003cRecord\u003cNewsfeedLanguage, { \"completeCount\": number, \"totalCount\": number, \"lastUpdate\": string }\u003e\u003e`\n\n#### Autogenerated controllers\n\nMost other models get an autogenerated controller with multiple standard endpoints.\nAll names in paths are in `snake_case`.\n\n- **GET /api/v1/:model**\n  - Lists all available objects of a requested type\n  - Supports pagination, on-demand recursive relations, filtering and sorting (see below)\n  - Response: `{ \"meta\": \u003cpagination data, if paginated\u003e, \"data\": Model[] }`\n- **GET /api/v1/:model/:id**\n  - Returns the requested object\n  - Supports on-demand recursive relations\n  - Response: `{ \"data\": Model }`\n- **POST /api/v1/:model**\n  - **Requres authentication**\n  - Creates the requested object\n  - Requires a json request body with the object to create (full model)\n  - Response: `{ \"success\": true, \"data\": Model }`\n- **PATCH /api/v1/:model/:id**\n  - **Requres authentication**\n  - Updates the requested object\n  - Requires a json request body with the changes to apply (partial model)\n  - Response: `{ \"success\": true, \"data\": Model }`\n- **DELETE /api/v1/:model/:id**\n  - **Requres authentication**\n  - Deletes the requested object\n  - Response: `{ \"success\": true }`\n- **GET /api/v1/:model/:id/:crudRelation**\n  - Lists all objects associated with the requested object (`id`) via the specified relation (`crudRelation`)\n  - Supports pagination, on-demand recursive relations (relative to the requested relation), filtering and sorting\n  - Response: `{ \"meta\": \u003cpagination data, if paginated\u003e, \"data\": RelatedModel[] }`\n- **POST /api/v1/:model/:id/:crudRelation**\n  - **Requires authentication**\n  - **Only applies to 1:n relations (hasMany)**\n  - Creates a new object (from json body) associated with the requested object (`id`) via the specified relation (`crudRelation`)\n  - Requires a json request body with the object to create (full model, -fk)\n  - Response: `{ \"success\": true, \"data\": RelatedModel }`\n- **POST /api/v1/:model/:mainId/:crudRelation/:relatedId**\n  - **Requires authentication**\n  - **Only applies to n:m relations (manyToMany)**\n  - Attaches an existing object (`relatedId`) to the requested object (`mainId`) via the specified relation (`crudRelation`)\n  - Requires a json request body with any non-autoGenerated pivot fields.\n  - Response: `{ \"success\": true }`\n- **DELETE /api/v1/:model/:mainId/:crudRelation/:relatedId**\n  - **Requires authentication**\n  - **Only applies to n:m relations (manyToMany)**\n  - Removes the relation (`crudRelation`) between the related object (`relatedId`) and the requested object (`mainId`)\n  - If the related object is attached multiple times, all attachments are removed by default\n    - Pivot properties marked as `detachFilter: true` can be specified via query params or json body as filters to limit which attachments are removed\n  - Response: `{ \"success\": true, \"numDetached\": \u003cnumber\u003e }`\n\n##### What model properties can I send?\n\nTo find out what properties are allowed for each model in json bodies, see the respective model definition files. (in `/app/models`)\n\nAll endpoints that require a model in the json body recognize all column fields that aren't considered `autoGenerated`.\nA field is considered `autoGenerated` if:\n\n- it is explicitly marked as `autoGenerated: true`, or\n- it is makred as `isPrimary: true`, `autoCreate: true` or `autoUpdate: true`, and is NOT marked as `autoGenerated: false`.\n\nAdditionally, endpoints marked with _`-fk`_ ignore the corresponding foreign key of the requested relation.\nThis field will be automatically set to the primary key of the main object. (`mainId`)\n\nEndpoints marked with _full model_ require that you send ALL recognized column fields that aren't marked with `optional: true`.\u003cbr\u003e\nEndpoints marked with _partial model_ consider all fields to be optional.\n\n##### What relations can I request?\n\nTo find out which relations are valid for each controller, see that controller's definition file. (in `/app/controllers`)\n\nFor on-demand recursive relations, look at the `queryRelations` array.\u003cbr\u003e\nFor relations specified as a path variable, look at the `crudRelations` array.\n\n##### How does pagination work?\n\nAll autogenerated enpoints are **not** paginated by default - pagination must be explicitly requested.\n\nTo request pagination on a supported endpoint, set the `page` query parameter to a positive integer.\nBy default, a page size of `10` is used. Set the `limit` query parameter to set a custom page size.\n\nWhen pagination is requested, pagination metadata will be returned in the `meta` field.\nThis field will include information such as:\n\n- `total` - the total record count\n- `lastPage` - the total number of pages\n\nFor example, to request the `5`th page of buildings, with `15` buildings per page:\u003cbr\u003e\n`GET https://api.topwr.solvro.pl/api/v1/buildings?page=5\u0026limit=15`\n\n##### How do on-demand relations work?\n\nSome autogenerated endpoints support on-demand recursive relations.\nThese allow you to fetch related objects along with the main object, in a single API request.\n\nTo request a relation, set its name to `true` in the query parameters.\nRecursive relation chains contain `.` in their names.\nIf you request a relation chain, any relations along the way will be requested and returned as well.\n\nArrays of related objects will be attached to a property with the relation's name.\n\nExample:\n\n- Request: `GET https://api.topwr.solvro.pl/api/v1/campuses/1?buildings.aeds=true`\n- Response:\n  ```json\n  {\n    \"id\": 1,\n    ...campus properties...,\n    \"buildings\": [\n      {\n        \"id\": ...,\n        \"campusId\": 1,\n        ...building properties...,\n        \"aeds\": [\n          {\n            \"id\": ...,\n            \"buildingId\": 1,\n            ...aed properties...\n          }\n        ]\n      },\n      ...more buildings...\n    ]\n  }\n  ```\n\n##### How do I request custom sorting?\n\nTo request the response to be sorted by a specified key, add a `sort` query parameter.\n\nTo sort in ascending order, use `+\u003ccolumn_name\u003e`.\nTo sort in descending order, use `-\u003ccolumn_name\u003e`.\n\nFor example, to sort campuses by `name`, in ascending order, send the following request:\u003cbr\u003e\n`GET https://api.topwr.solvro.pl/api/v1/campuses?sort=+name`\n\n##### How does filtering work?\n\nTo request filtering, pass property names \u0026 predicates as query parameter keys and values.\nProperty names should use the same casing as in model definition files. (camelCase)\n\nFor strings properties, the values are matched against the predicates using `ILIKE` - this means the filtering is **case insensitive**, and **`%` can be used as a wildcard**.\nOther types of properties are matched using direct equality.\n\nExamples:\n\n- get all fields of study that belong to department number six:\u003cbr\u003e\n  `GET https://api.topwr.solvro.pl/api/v1/fields_of_study?departmentId=6`\n- get all contributors' youtube links:\u003cbr\u003e\n  `GET https://api.topwr.solvro.pl/api/v1/contributor_social_links?linkType=youtu`\n- get all campuses whose names end with `a`:\u003cbr\u003e\n  `GET https://api.topwr.solro.pl/api/v1/campuses?name=%a`\n\nAny numeric or date-time field can be filtered by range. Pass the following query params to filter by range:\n\n- for lower bound of greater or equal to value: `\u003cparam\u003e.from=\u003cvalue\u003e`\n- for upper bound of lesser or equal to value: `\u003cparam\u003e.to=\u003cvalue\u003e`\n- for both lower and upper bound, pass both params separately (that is: `\u003cparam\u003e.from=\u003cvalue\u003e\u0026\u003cparam\u003e.to=\u003cvalue\u003e`)\n\nYou can pass both `from` lower bound and `to` upper bound or only one of them.\nIf `from` is equal to `to`, the filter works exactly like the single value filter (in other words: `departmentId.from=1\u0026departmentId.to=1` is equal to `departmentId=1`).\n\nExample of a correctly formed request:  \nGoal: Fields of study belonging to departments with ids ranging from two to four, both ends inclusive\nRequest: `GET https://api.topwr.solvro.pl/api/v1/fields_of_study?departmentId.from=2\u0026departmentId.to=4`\n\n### Errors\n\nAny errors will be indicated by a status code from the `4xx` or `5xx` range.\n`4xx` errors are caused by invalid or incomplete requests, while `5xx` errors are server errors.\n\nAll error responses follow a standardised format, defined and documented in-depth in [/app/exceptions/base_error.ts](https://github.com/Solvro/backend-topwr/blob/2630637842b4ca61d6fc215b5eb90ebee2e8a37a/app/exceptions/base_error.ts#L369-L408)\n\n## Links\n\n[![docs.solvro.pl](https://i.imgur.com/fuV0gra.png)](https://docs.solvro.pl)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsolvro%2Fbackend-topwr","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsolvro%2Fbackend-topwr","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsolvro%2Fbackend-topwr/lists"}