**این سؤال تنها با زبانهای _PHP_ ، _Python_ ، _Go_ و _JS (Node.js)_ قابل حل است.**
---
دیجیکالا قصد دارد برای بخش تحویل محصولات خود یک سامانهی سادهی ثبت پیشنهادات و انتقادات راهاندازی کند. از شما میخواهیم یک *API* برای این سامانه طراحی کنید.
# جزئیات پروژه
پروژهی اولیه را از [این لینک](/problemset/assignments/4367/download_problem_initial_project/148124/) دانلود کنید.
در این سؤال، یک *REST API* شامل *endpoint* های زیر باید پیادهسازی شود:
| آدرس | عنوان |
| :------------------------ | ----------------------: |
| `GET /` | بررسی *up* بودن سرویس |
| `POST /signup` | ثبتنام |
| `POST /login` | ورود به حساب کاربری |
| `POST /suggestions` | درج پیشنهاد یا انتقاد |
| `GET /suggestions` | دریافت لیست پیشنهادات و انتقادات |
در این *API* هر کاربر باید یک توکن داشته باشد. این توکن برای هر کاربر ثابت است.
%align_right_start%
## *endpoint* های موردنیاز
%align_end%
**در همهی _endpoint_ ها، پاسخ باید بهصورت _JSON_ باشد.**
اطلاعات ورودی بهصورت `application/x-www-form-urlencoded` به *endpoint* ها ارسال میشوند.
### بررسی *up* بودن سرویس
پاسخ این *endpoint* باید بهصورت زیر باشد:
+ کد وضعیت: `200`
+ بدنه: `{"ok":true}`
### ثبتنام
دو پارامتر `username` و `password` باید به این *endpoint* ارسال شوند. در صورتی که حداقل یکی از این پارامترها ارسال نشده باشد یا برابر با رشتهی خالی باشد، پاسخ باید بهصورت زیر باشد:
+ کد وضعیت: `400`
+ بدنه: `{"ok":false,"error":"no username or password provided"}`
اگر کاربری با نام کاربری واردشده از قبل موجود باشد، پاسخ باید بهصورت زیر باشد:
+ کد وضعیت: `400`
+ بدنه: `{"ok":false,"error":"user already exists"}`
در غیر اینصورت، کاربر باید ساخته شود، یک توکن یکتا برایش تولید شود و پاسخ بهصورت زیر باشد:
+ کد وضعیت: `201`
+ بدنه: `{"ok":true,"token":"<mark title="توکن کاربر">USER_TOKEN</mark>"}`
### ورود به حساب کاربری
دو پارامتر `username` و `password` باید به این *endpoint* ارسال شوند. در صورتی که حداقل یکی از این پارامترها ارسال نشده باشد یا برابر با رشتهی خالی باشد، پاسخ باید بهصورت زیر باشد:
+ کد وضعیت: `400`
+ بدنه: `{"ok":false,"error":"no username or password provided"}`
اگر نام کاربری یا رمز عبور نادرست باشد، پاسخ باید بهصورت زیر باشد:
+ کد وضعیت: `400`
+ بدنه: `{"ok":false,"error":"invalid username or password"}`
در غیر اینصورت، پاسخ باید بهصورت زیر باشد:
+ کد وضعیت: `200`
+ بدنه: `{"ok":true,"token":"<mark title="توکن کاربر">USER_TOKEN</mark>"}`
### درج پیشنهاد یا انتقاد
این *endpoint* نیازمند *authentication* است. در ریکوئست ارسالی مقدار هدر `Authorization` باید برابر با توکن کاربر باشد (**بدون `Bearer` یا موارد مشابه**).
پارامتر `text` (متن پیشنهاد یا انتقاد) باید به این *endpoint* ارسال شود. در صورتی که این پارامتر ارسال نشده باشد یا برابر با رشتهی خالی باشد، پاسخ باید بهصورت زیر باشد:
+ کد وضعیت: `400`
+ بدنه: `{"ok":false,"error":"no text provided"}`
در غیر اینصورت، پیشنهاد یا انتقاد باید ثبت شود و پاسخ بهصورت زیر باشد:
+ کد وضعیت: `201`
+ بدنه: `{"ok":true}`
### دریافت لیست پیشنهادات و انتقادات
این *endpoint* باید لیست پیشنهادات و انتقادات ثبتشده در سامانه را در قالب یک لیست برگرداند. این لیست شامل پیشنهادات و انتقادات ثبتشده توسط همهی کاربران است.
+ کد وضعیت: `200`
+ مثالی از پاسخ:
```json
[
{
"user": "username1",
"text": "sample suggestion 1"
},
{
"user": "username2",
"text": "sample suggestion 2"
}
]
```
# نکات تکمیلی
<details class="blue">
<summary>
نصب نیازمندیها و اجرا
</summary>
برای حل این سؤال میتوانید از هر زبان و هر تکنولوژیای که میخواهید استفاده کنید. بهصورتی که در یک پوشه به نام `api` کد برنامه را نوشته و در فایلی به نام `runner.sh` که توسط `sh` اجرا میشود، باید برنامهی خود را اجرا کنید. توجه کنید که حتماً باید `Dockerfile` مربوط به پروژهی خود را برای ما ارسال کنید.
در پروژهی اولیه، ۴ داکرفایل برای `php`، `python`، `golang` و `node` قرار دادیم که میتوانید از آنها مستقیماً استفاده کنید. در صورتی که از یکی از این زبانها برای حل سؤال استفاده میکنید، کافیست که `Dockerfile` مربوط به آن را در پوشهی `api` کپی کنید و طبق توضیحات داده شده، سؤال را حل کنید. برای نصب نیازمندیهای پایتون از `requirements.txt`، برای پیاچپی از `composer.json`، برای گولنگ از `go.mod` و برای نودجیاس از `package.json` استفاده کنید.
در صورتی که زبان مورد استفادهی شما، چیزی به جز این ۴ مورد است، باید خودتان داکرفایلی در پوشهی `api` بهشکلی بنویسید که بتواند نیازمندیهای پروژهی شما را نصب کرده و برنامهی شما را مانند داکرفایلهای موجود اجرا کند.
</details>
+ نیازی به *persistent* بودن دادهها نیست!
+ سیستم داوری `docker-compose.yml` زیر را خارج از فولدر `api` پاسخ شما قرار میدهد و با دستور `docker-compose up --build` آن را اجرا میکند.
```yaml docker-compose.yml
version: "3"
services:
api:
build: "./api"
container_name: "api"
ports:
- "80:80"
```
+ شما مجاز به تغییر یا ارسال `docker-compose.yml` دلخواه نیستید.
+ سرویس شما باید روی پورت `80` آدرس `localhost` قابل دسترسی باشد.
+ توصیه میکنیم در `runner.sh` خود *API*تان را روی `0.0.0.0:80` اجرا کنید.
<details class="green">
<summary>
تغییر `Dockerfile`
</summary>
امکان تغییر فایل `Dockerfile` وجود ندارد، اما در اسکریپت `runner.sh` میتوانید هر دستوری را اجرا کنید.
</details>
## نحوه ارسال پاسخ
شما میتوانید تمامی محتوای موجود در پوشهی `api` را تغییر دهید و هر فایلی که میخواهید اضافه یا کم کنید.
```text
api
├── <mark class="purple" title="نام این فایل اهمیتی ندارد"> api.py </mark> # or main.go somefile.js anyfile.php name.any ...
├── Dockerfile
├── requirements.txt # or go.mod package.json composer.json
└── runner.sh
```
توجه کنید که نام فایل کد شما برای سیستم داوری اهمیتی ندارد و این خود شما هستید که در `runner.sh` از نام آن برای اجرای پروژه استفاده میکنید.
در نهایت این پوشه را _zip_ کرده و ارسال کنید. توجه کنید که پس از _extract_ کردن فایل _zip_ شما، باید پوشهی `api` را ببینیم که درون آن `Dockerfile` وجود دارد.
سامانه ثبت پیشنهادات و انتقادات