A time-tracker born out of the "we don't know Clojure yet" dialog. Idea: have a web server backend so teamleads and users themselves can view time spent on tasks, have a desktop app to do the tracking.
Essentially, what we'd want is a simple project to learn Clojure.
It'll need:
- routes to log in and out
- basic admin panel
- ability to create and delete projects
- ability to start tracking time on a project
- Probably microframework-based, and Luminus seems nice on the first sight: what I like is REPL-driven development idea, what I want to know later is modularity of Luminus (replacing a template engine, for instance)
- JWT auth routes (People in this thread recommend buddy)
- Admin panel to add and delete users (yes, manually, we can improve it later)
- Route to start and stop tracking time
The application will be browser-based.
Also, it would be sehr cool, if we will be able to implement client-side in ClojureScript, transpilable or in other way compatible with one of popular JS frameworks: Vue.js, React or Angular.
Login:
- ask user for the server URL, login, password
- log on a server using supplied login and password
- open the base view
Main view:
- show a list of the projects, a button to create one and delete one
- when a project is picked, enable "start" button
- track when user has no activity, show a pause notification, pause tracking in 1 minute
Let's stop on a simple email-based authentication, with a possible username.
Columns:
id
(serial
, autoincremented integer type)email
(text, discuss what limit we impose on its length)username
(text, discuss what limit we imposse on its length)salt
(autogenerated password salt for security reasons)salt_num
(how many times did iterative hashing happen?)password
(salted password checksum)
Scenario: user registers, a hash is concatenated with new password, random number salt_num
is picked (2-9), password is hashed with salt salt_num
times, so rainbow tables use on a leaked db is much less useful. (Yes, it's a typical modern approach.)
We should store all user sessions somewhere. The fact we'd store them in a DB is awkward, but this app is small.
Columns
id
(serial
, autoincremented integer type)user_id
(foreign key to `auth)token
(text)token_datetime
(datetime type for current DB implementation)
We need groups, created by users, as an abstraction to unite people: companies are not the best principle to unite, since they can create confusion. RBAC is too complex.
id
(serial
, autoincremented integer type)name
(text
, name of a group, may have a character length limit or not, up to discussion)description
(text
, a custom text, describing a group)
id
(serial
) - autoincremented integer type)group_id
(foreign key toauth_group
id)name
(text
) - name of a project, may have a character length limit or not, up to discussion)
id
(serial
) - autoincremented integer type)user_id
(a foreign key to user->id)project_id
(a foreign key to projects)mode
(bool) - do we start tracking, or stop trackingdatetime
- datetime for a tracking events
id
(serial
, autoincremented integer type)user_id
(int
) - a foreign key to usergroup_id
(int
) - a foreign key to a groupis_owner
(bool
) - can add users to groups, remove a group, rename a group, view timings inside a groupis_mod
(bool
) - can view a list of people in a group, view timings for other users
id
(serial
, autoincremented integer type)inviter_id
- a foreign key to inviting usergroup_id
- a foreign key to group the user is invited intoinvitee_id
- a foreign key to user invited to a group
/api/auth/register/
- let's not implement email verification yet, accept email and password, check if there is no such email in use, create anauth
table record./api/auth/login/
- verify a password by iterative hashing, return an error on failure and an auth token on success (JWT routes). Example request:Content-Type: application/json
(header),{"username": "Petya", "password":"IAm1st"}
or{"email": "[email protected]", "password":"IAm1st"}
(body)./api/auth/refresh/
- refresh a token (JWT). Example:Content-Type: application/json
(header),{"token":"<JWT>"}
(body)./api/auth/verify
- we've closed an app and reopened it, we have the token, it did not expire, so we supply it to login instead.Content-Type: application/json
(header),{"token":"<EXISTING_TOKEN>"}
(body).
/api/tracking/<project_id>/start
- start tracking time for a user on<project_id>
/api/groups/send_invite
- receive a user email as a string or user id, invite user to a group/api/groups/check_invites
- check for existing invites, needs a regular (1 min) refresh interval and makes me think that tracking events and this part fit into a common WebSocket API, to be honest./api/groups/accept_invite
- accepts an invite notification/api/groups/add
- receive a user id, group name, create a new group, return its id/api/groups/remove
- receive a group id, user id, session token, check if user can remove group, remove if possible, return an error otherwise/api/groups/rename
- receive a group id, user id, session token, check if user has rights to remove a group, return an error otherwise/api/groups/list_users/
- receive a group id, user id, session token, check if user has rights to see the members, list all members in a group/api/groups/view_timings/user_id
- view timings for a certain user, maybe will need pagination at some point, but in the beginning it's possible to return all
/api/projects/list/
- supply user id, group id; check user rights, show list of projects in a group/api/projects/add
- supply user id, group id, new project name; add project for a group/api/projects/
- remove a project from a group
/api/tracking/start/
- supply project id, user id, stop all active tracking, start tracking on a project if it's in user's groups/api/tracking/stop
- supply project id, user id, stop tracking on a selected project/api/tracking/project/count
- supply user id, project id, count all time spent time on a project/api/tracking/count_global
- supply user id, count global time on all projects