This is an educational project. The task is here - https://coda.io/@metalamp/education/4-15
I took this example as the basis of the project (use servant and postgresql-simple) - https://github.com/jinilover/type-safe-ws
I took this example as the basis of the project (use handle pattern for logging and config) - https://github.com/fullstack-development/haskell-internship/tree/master/echo-bot-template
You can use the links to study (review) some of the topics for this project. They are placed in the text, and in # References
What can be improved in # ToDoList
library | Used for |
---|---|
сonfigurator | to work with config |
time | to work with time in logging and creation news/users |
servant | for writing type-safe web applications |
Data.Pool | to work with pool |
postgresql-simple | for use the PostgreSQL database |
cryptonite | for password hashing |
If you have never worked with servant - https://docs.servant.dev/en/stable/ https://github.com/sras/servant-examples
Attention please! You must have a Postgres on your computer! And you must to create a data base, user, password, port. You can see the example in config.conf and use the same values.
config {
# DbConfig
dbHost = "localhost" # "localhost" - dbHost for test server
dbName = "tiny" # "news" - dbName in postgress
user = "postgres" # "postgres" - user in postgress
password = "postgres123" # "postgres123" - password in n postgress
dbPort = 5432 # 5432 - dbPort in postgress
noOfStripes = 2 # 2 - stripes https://docs.servant.dev/en/stable/cookbook/db-postgres-pool/PostgresPool.html
idleTime = 60 # 60 - unused connections are kept open for a minute
stripeSize = 10 # 10 - max. 10 connections open per stripe
}
If you have never worked with postgres - https://learn.coderslang.com/ru/0119-setting-up-and-getting-started-with-postgresql/
Attention please! You must have the config.conf file at the root of the project. If file config.conf not exist, you must not run server. File logs.txt created automatically for logging (If you choose logging in file) You must set some parameters in the config.conf:
stack build
stack exec news-exe
stack test
You can use it such as curl request. If you have never worked with curl - https://curl.se/docs/manual.html
All migrations are at migrations.
Before starting the server, you need to create a new empty database in the Postgres DB.
During migrations, a user with login "polina" and password "polina" is automatically created (Author and administrator). You can create other administrators and authors, if you login as "polina" in request :
--header "Cookie: servant-auth-cookie=keypolina"
You can change in _migrations/01.sql the login and password (In this case, you also need to change in _migrations/02.sql the token login (== login) and token key (=="key" ++ login)) for the first user in server.
You must generating a password hash and a token key by app/MainBase64.hs.
The name of the database and the owner put in the config:
config {
# DbConfig
dbName = "tiny" # "tiny" - dbName in postgress
user = "postgres" # "postgres" - user in postgress
}
Schema is at DatabaseSchema
I explain only content of some folders and files. In other places content is standard. Folders with prefixed "_" are used for documentation and running/testing server (examples requests or content and do not contain haskell code).
news/
│ _docs/ # Depiction EndPoints for readme doc
│ │
│ _image/ # Images in png and base64 for example
│ │
│ _migrations/ # Contain empty scheme for Data Base
│ │
│ _scripts/ # One request for each endpoint
│ │
│ _scripts_error/ # Some requests for testing error codes
│ │
│ _requests/ # Many requests for get news endpoints (use filters & sort & limit & offset)
│ │
│ app/
│ │
│ ├── Main.hs
│ ├── MainBase64.hs # Convert file from png to base64
│ ├── MainCR.hs # Generate hash for password, token key
│ │
│ src/
│ │
│ ├── EndPoints/
│ │ ├── Lib/ # Lib for End Points
│ │ │ ├── Category/ # Lib for Category End Points
│ │ │ ├── News/ # Lib for News End Points
│ │ │ ├── Lib.hs # Some function for many End Points
│ │ │ ├── OffsetLimit.hs # Offset limit for End Points
│ │ │ ├── ThrowError.hs # Class for throw requests errors
│ │ │ ├── ToHttpResponse.hs # class ToHttpResponse for throwError by servant
│ │ │ └── ToText.hs # class ToText.hs for present in logging results of requests
│ │ ├── AddOneCategory.hs # Create one category (authentication admin required)
│ │ ├── AddOneImage.hs # Create a path to one loaded image (authentication author required)
│ │ ├── AddOneNews.hs # Create one news (authentication author required)
│ │ ├── AddOneUser.hs # Create one user (authentication admin required)
│ │ ├── EditOneCategory.hs # Edit one category (authentication admin required)
│ │ ├── EditOneNews.hs # Edit one news (authentication author required)
│ │ ├── GetAuthorsNewsList.hs # Get news list after filers and sorts (authentication author required)
│ │ ├── GetAuthorsNewsSearchList.hs # Get news list after search (authentication author required)
│ │ ├── GetCategoryList.hs # Get a list of categories
│ │ ├── GetNewsList.hs # Get news list after filers and sorts
│ │ ├── GetNewsSearchList.hs # Get news list after search
│ │ ├── GetOneImage.hs # Get a one image
│ │ └── GetUserList.hs # Get a list of users
│ │
│ ├── Logger/
│ │ └── Impl.hs # The default implementation of the Logger interface
│ │
│ ├── Types/
│ │ ├── ApiTypes.hs # RestAPI
│ │ ├── DataTypes.hs # all types except api, config, error
│ │ ├── ErrorTypes.hs # Error for endpoints
│ │ └── ExceptionTypes.hs # Exceptions
│ │
│ ├── Config.hs # Configuration reader and default config value
│ ├── DbServices.hs # Connection pool to DB used in module Server
│ ├── Logger.hs # The logger interface module. Not define an implementation
│ ├── News.hs # Handle for config and logger
│ └── Server.hs # run server and authorization (servant-auth-cookie)
│
├── config.conf # config file
└── logs # File with logs (created automatically)
Simple hello from server. Example:
curl -v http://localhost:8080/
For some get methods (return list) can use Offset&Limit
I define general list of errors, and errors list for each endpoint or group of endpoints. See here src/Types/ErrorTypes.hs
Code | Description |
---|---|
400 | Bad Request (Invalid limit or offset, invalid content by type or value) |
403 | Forbidden Error (Invalid author permission) |
404 | Not Found (Invalid admin permission, Invalid id) |
500 | Internal Server Error (Error of executing SQL request) |
Thanks to my mentors Pavel and Roman , I could to close all ToDoList еxcept the first:
"Exception: libpq: failed (connection to server at "localhost" (::1), port 5432 failed: FATAL: database "news2" does not exist")
"Exception: logs: openFile: resource busy (file is locked)"
* The terminal always displays authorization data, most likely it is from src/Server.hs
[] [("\1050\1072\1084\1080\1085 \1040\1074\1077\1083\1080\1085\1072",2022-12-02,"polina","1","\1040\1074\1077\1083\1080\1085\1072","\1061\1086\1088\1086\1096\1080\1081 \1082\1072\1084\1080\1085 \1087\1086\1089\1090\1088\1086\1080\1083 \1057\1077\1088\1075\1077\1081.",PGArray {fromPGArray = [8,9,10]},3,True)] [("\1050\1072\1084\1080\1085 \1040\1074\1077\1083\1080\1085\1072",2022-12-02,"polina","1","\1040\1074\1077\1083\1080\1085\1072","\1061\1086\1088\1086\1096\1080\1081 \1082\1072\1084\1080\1085 \1087\1086\1089\1090\1088\1086\1080\1083 \1057\1077\1088\1075\1077\1081.",PGArray {fromPGArray = [8,9,10]},3,True)]
* Add monad transformers to do with arrays such us [IO Either a b]