Today or never
This program is a service to manage your tasks, with a focus on what you can do today.
It is a REST API implementing the following features:
- JSON responses
- JWT authentication
- CORS protection
- Rate limiting
- Request logging
- Observability
🛠️ Installation
Create a configuration file
Create a .env
file with the following content and adapt the values
# Service configuration
SERVICE_HOST=localhost
SERVICE_PORT=8080
# Database configuration
DATABASE_ENGINE=sqlite3
DATABASE_DSN=./todayorneverd.db
# Logger minimum level
# See https://github.com/rs/zerolog?tab=readme-ov-file#leveled-logging
LOGGER_LEVEL=0
# JWT based authentication
AUTH_ISSUER=todayornever-api
AUTH_SECRET=<REDACTED> # a long and random string
AUTH_EXPIRES=1 # in hours
Compiling
Compiling requires the installation of make, golang and SQLite3
make
Running
./todayornever-api
🎯 API endpoints
Authentication
POST
/users/signup
(create a new user)
Parameters
name | location | data type | mandatory | description |
---|
username | JSON body | string | yes | A valid string with 3 characters minimum |
email | JSON body | string | yes | A valid email |
password | JSON body | string | yes | A valid password |
Responses
http code | content-type | response |
---|
200 | application/json | A user object |
400 | application/json | {"code":"400","message":"Bad Request"} |
401 | application/json | {"code":"401","message":"Unauthorized"} |
Example
curl -X POST -H "Content-Type: application/json" --data '{"username": "example", "email": "[email protected]", "password": "P4sSw0rd~"}' http://localhost:8080/users/signup
POST
/users/login
(login with an existing user)
Parameters
name | location | data type | mandatory | description |
---|
email | JSON body | string | yes | A valid email |
password | JSON body | string | yes | A valid password |
Responses
http code | content-type | response |
---|
200 | application/json | {"token": "<token>", "expires": "<timestamp>"} |
400 | application/json | {"code":"400","message":"Bad Request"} |
401 | application/json | {"code":"401","message":"Unauthorized"} |
Example
curl -X POST -H "Content-Type: application/json" --data '{"email": "[email protected]", "password": "P4sSw0rd~"}' http://localhost:8080/users/login
Projects
GET
/projects
(list all projects)
Responses
http code | content-type | response |
---|
200 | application/json | An array of project objects |
400 | application/json | {"code":"400","message":"Bad Request"} |
401 | application/json | {"code":"401","message":"Unauthorized"} |
Example
curl -H "Authorization: Bearer <token>" http://localhost:8080/projects
GET
/projects/{id}
(Fetch a project identified by {id})
Parameters
name | location | data type | mandatory | description |
---|
id | URI | numeric | yes | A valid integer |
Responses
http code | content-type | response |
---|
200 | application/json | A project object |
400 | application/json | {"code":"400","message":"Bad Request"} |
401 | application/json | {"code":"401","message":"Unauthorized"} |
Example
curl -H "Authorization: Bearer <token>" http://localhost:8080/projects/1
POST
/projects
(create a new project)
Parameters
name | location | data type | mandatory | description |
---|
name | JSON body | string | yes | A valid string of 3 characters minimum |
description | JSON body | string | no | A valid string of 3 characters minimum |
Responses
http code | content-type | response |
---|
201 | application/json | A project object |
400 | application/json | {"code":"400","message":"Bad Request"} |
401 | application/json | {"code":"401","message":"Unauthorized"} |
Example
curl -X POST -H "Authorization: Bearer <token>" -H "Content-Type: application/json" --data '{"name": "Project 1"}' http://localhost:8080/projects
PATCH
/projects/{id}
(update a project identified by {id})
Parameters
name | location | data type | mandatory | description |
---|
id | URI | numeric | yes | A valid integer |
name | JSON body | string | no | A valid string of 3 characters minimum |
description | JSON body | string | no | A valid string of 3 characters minimum |
Responses
http code | content-type | response |
---|
200 | application/json | A project object |
400 | application/json | {"code":"400","message":"Bad Request"} |
401 | application/json | {"code":"401","message":"Unauthorized"} |
Example
curl -X PATH -H "Authorization: Bearer <token>" -H "Content-Type: application/json" --data '{"name": "Edited project 1"}' http://localhost:8080/projects/1
DELETE
/projects/{id}
(delete a project identified by {id})
Parameters
name | location | data type | mandatory | description |
---|
id | URI | numeric | yes | A valid integer |
Responses
http code | content-type | response |
---|
204 | application/json | No Content |
400 | application/json | {"code":"400","message":"Bad Request"} |
401 | application/json | {"code":"401","message":"Unauthorized"} |
Example
curl -X DELETE -H "Authorization: Bearer <token>" http://localhost:8080/projects/1
Tasks
GET
/tasks
(list all tasks)
Responses
http code | content-type | response |
---|
200 | application/json | An array of task objects |
400 | application/json | {"code":"400","message":"Bad Request"} |
401 | application/json | {"code":"401","message":"Unauthorized"} |
Example
curl -H "Authorization: Bearer <token>" http://localhost:8080/tasks
GET
/tasks/{id}
(Fetch a task identified by {id})
Parameters
name | location | data type | mandatory | description |
---|
id | URI | numeric | yes | A valid integer |
Responses
http code | content-type | response |
---|
200 | application/json | A task object |
400 | application/json | {"code":"400","message":"Bad Request"} |
401 | application/json | {"code":"401","message":"Unauthorized"} |
Example
curl -H "Authorization: Bearer <token>" http://localhost:8080/tasks/1
POST
/tasks
(create a new task)
Parameters
name | location | data type | mandatory | description |
---|
title | JSON body | string | yes | A valid string of 3 characters minimum |
description | JSON body | string | no | A valid string of 3 characters minimum |
Responses
http code | content-type | response |
---|
201 | application/json | A task object |
400 | application/json | {"code":"400","message":"Bad Request"} |
401 | application/json | {"code":"401","message":"Unauthorized"} |
Example
curl -X POST -H "Authorization: Bearer <token>" -H "Content-Type: application/json" --data '{"title": "Task 1"}' http://localhost:8080/tasks
PATCH
/tasks/{id}
(update a task identified by {id})
Parameters
name | location | data type | mandatory | description |
---|
id | URI | numeric | yes | A valid integer |
title | JSON body | string | no | A valid string of 3 characters minimum |
description | JSON body | string | no | A valid string of 3 characters minimum |
Responses
http code | content-type | response |
---|
200 | application/json | A task object |
400 | application/json | {"code":"400","message":"Bad Request"} |
401 | application/json | {"code":"401","message":"Unauthorized"} |
Example
curl -X PATH -H "Authorization: Bearer <token>" -H "Content-Type: application/json" --data '{"title": "Edited task 1"}' http://localhost:8080/tasks/1
DELETE
/tasks/{id}
(delete a task identified by {id})
Parameters
name | location | data type | mandatory | description |
---|
id | URI | numeric | yes | A valid integer |
Responses
http code | content-type | response |
---|
204 | application/json | No Content |
400 | application/json | {"code":"400","message":"Bad Request"} |
401 | application/json | {"code":"401","message":"Unauthorized"} |
Example
curl -X DELETE -H "Authorization: Bearer <token>" http://localhost:8080/tasks/1
💾 Data model
User model
property | type | description |
---|
id | numeric | An identifier generated at the creation |
username | string | |
email | string | |
password_hash | string | Generated at creation |
salt | string | Generated at creation |
state | string | |
created_at | datetime | |
updated_at | datetime | |
Project model
property | type | description |
---|
id | numeric | An identifier generated at the creation |
user_id | numeric | A reference to the owner |
name | string | |
description | string | |
position | numeric | |
created_at | datetime | |
updated_at | datetime | |
Task model
property | type | description |
---|
id | numeric | An identifier generated at the creation |
user_id | numeric | A reference to the owner |
project_id | numeric | A reference to the associated project |
parent_task_id | numeric | A reference to the parent task |
title | string | |
description | string | |
state | string | |
position | numeric | |
overdue | boolean | |
due_at | datetime | |
created_at | datetime | |
updated_at | datetime | |
📡 Observability
The logger is configured to send events to OpenObserve if the appropriate configuration variables are set.
🔖 License
This software is distributed under the MIT license.