CHANGELOG.md Β· Frontend (React/TypeScript) Β· Backend (Django/Python)
Hey. π
I didn't have any solo-dev full-stack sample app in my portfolio, so now I have. This project was done in 4 days for one of the recruitment process steps.
Launch bash and type in...
./scripts/one_script_wonder.sh
...website available at localhost:8000!
- Implementation Technologies:
- βοΈ Backend:
- ποΈ Frontend:
- TypeScript 4.6
- React 17.0
- React Redux 7.2
- React Bootstrap 2.2
- Webpack 5.70
- π² Communication:
- REST API
- βοΈ Backend: Django Rest Framework
- ποΈ Frontend: Axios
- REST API
- π· Continuous Integration:
- Implementation Design:
- βοΈ Backend:
- "Screaming" Architecture
- ποΈ Frontend:
- Self-Checkout Hierarchy
- Tiny Components
- Separation of Concerns
- ποΈ Docs:
- Medium Commits
- Tags
CHANGELOG.md
- π§ Dev-Style:
- Deadline Driven Development
- (a pinch of time compared to how much there should be for such a project)
- Deadline Driven Development
- βοΈ Backend:
- Functionalities
- Responsiveness
- Registration / login
- Token Authorization
- Users can create budgets
- Users can manage users assigned to a given budget
- Tests
- Pagination
- Filtering
- Multi-language Support
You can manage the project via shell scripts (that can be launched on Unix systems or w/ git bash on Windows). These scripts contain some common routines that you might be interested in.
./scripts/one_script_wonder.sh
- Runs the whole project from scratch. Initializes, makes database migrations and starts containers.
./scripts/initialize.sh
- Created Docker Volume nad initializes the project (no-cache).
./scripts/setup.sh
- Builds Frontend and Backend
./scripts/migration.sh
- Does the
python manage.py makemigrations
&&python manage.py migrate
on docker container.
- Does the
./scripts/rebuild.sh
- Same as
one_script_wonder.sh
but w/o Volume creating and using cache.
- Same as
./scripts/up.sh
- Basically
docker-compose -d up
.
- Basically
There are lots of files, I know. Tl;dr, these are three rules on frontend:
- No Semicolons
- No Spaces between the methods name and parenthesis
- Double Quotes
It's quite simple to understand, refer to this pseudo-UML diagram below. It contains model, interaction, and implementation perspectives.
All the communication is handled by Services (*.service.ts
), which can be found in the /frontend/src/services/
directory. Even though this project is made with React framework, this is the idea that I came across when doing some Angular projects.
These services can be used by anyone, but in this app, everything is exclusively handled by the State Creators - they are placed inside the /frontend/src/store/
. More on Action Creators in the react-redux documentation.
The header of each response contains the user token if the user is logged in.
All endpoints start with /api/rest/
.
/auth/login/
(post)- Data:
{email: str, password: str}
- Logins the user with credentials if registered
- Data:
/auth/register/
(post)- Data
{email: str, password: str}
- Registers a new account with given credentials
- Note: Password string confronts
algorithm$salt$iterations$hashed_password
- Data
/auth/check/
(post)- Data
{email: str}
- Safety-check: is the user holding his own valid token?
- Data
/budget/
(post)- Data:
{name: str, total_budget: int}
- Creates a new budget
- requires auth token
- Data:
/budget/?searchQuery=<str>?page=<int>
(get)- Parametrized:
{searchQuery: str, page: int}
- Gets all budgets (or budgets whose title match search query) of currently logged in user paged by index page.
- requires auth token
- Parametrized:
/budget/users
(post)- Data:
{budget_id: str, users: list[{id: int, email: str}]}
- Updates the given budget with a new user list.
- requires auth token
- Data:
/budget/expense
(post)- Data:
{budget_id: int, name: str, amount: int, category: str}
- Creates a new Expense item for given budget.
- requires auth token
- Data:
/budget/income
(post)- Data:
{budget_id: int, name: str, amount: int, category: str}
- Creates a new Income item for given budget.
- requires auth token
- Data:
/users/all/
(get)- Queries all users from database.
- requires auth token
Aka. Views vs ViewSets: inter-app backend structure design.
- At first, I went with the
ViewSets
but with only a single oneView
inside of it - it was just for the ease of switching after making the final decision whether I would like the first or latter option. - I'm highly favoring architecture which screams the available use cases - it's just a good design principle.
- After all, I've created a
RequestManagers
calledHandlers
, and those have their own separate directory inside of each Django App, that can inform about the possible use-cases from the perspective of the file hierarchy. This allows me to get the benefits of abstractingViews
intoViewSets
(commonized base paths or authentification/other stuff that Django gives in its features). - It's all debatable though, but that is just my take on that. Here's the structure explanation
The Django implicit way of working using the benefits of mixins and inheritance by just providing class-level attributes for serializing models and stuff is definitely solid primary solution.
This is the directory tree hierarchy of pages
:
landing
credentials-form
login-form
register-form
logged
budget-container
container-tabs
tab-searchbar
tab-add-budget
tab-item-budget
container-panes
pane-transaction
transaction-header
header-add-transaction
header-manage-users
transaction-table
container-pagination
As you can read, there are two pages: the landing page and logged page. The landing page contains a credential form that can be either login or register form. The logged page contains a budget-container that consists of tabs and panes. Tabs have budgets items and a search bar functionality, along with add budget functionality. The pane has transactions, which are made out of transaction header (containing add transaction functionality and manage users functionality) along with the table content.
Thus, the DOM Tree and domain implementation perspective is fully preserved.
- I would advocate to never send the password in plaintext via API, even when SSL is used. The best solution would be to generate a unique salt on the client-side, store it in the database, and then perform proper encryption before sending a request with a password. More good practices on REST API Communication here.
- The very same issue is with the Token authorization that I've implemented - it is not safe, especially because I keep it in localStorage. I would suggest changing the implementation from the Django token (about which I don't know, much and thus I definitely don't trust the mechanism) to something that has a viable reputation like the JWT Tokens. The very best solution would be to handle authentication by 3rd party system in which they know what they are doing.
- Currently, the passwords are encrypted via random salt and sha-256. This is not safe. Preferably use the AES or RSA. Generally, if this
README.md
is already "dated" compared to the time you are reading this, refer to OWASP for newest cybersecurity recommendations (password cheatsheet).
- Lots of stuff is done on the behalves of currently logged-in users. A ready-made solution for such tasks would be great. There is a django-crum library that contains built-in tests, safe-checks, and implementation, but unfortunately is not supported for Django 4.0.
Here's a PEP-0518 with answers.
Standardjs wants single parenthesis. This ' character might be confusing with ` backtick character. For this reason, I gave up the ts-standard library.