diff --git a/README.md b/README.md index 882f1a1..c62b5f6 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,34 @@ # Body track [![Build Status](https://ci.vdhsn.com/api/badges/adam/bodytrack/status.svg?ref=refs/heads/trunk)](https://ci.vdhsn.com/adam/bodytrack) + + +## Body track backend rules + +- Database commandments + 1. All measurement values are in metric units + - weight = kg + - height = cm + - calories = kcal + 2. All time values are in UTC + 3. All ip related values need to support both ipv4 and ipv6 + 4. Email addresses are unique per user + +- HTTP APIs specs + - Authentication + - JWTs: + - 14 day maximum lease time + - claims (userid) + - OAuth2: scopes (email, profile, stats, workouts) + - REST based design + - Allow export via CSV all personal data + - Allow deleting account data + - 80ms 95th percentile max response time for all calls + +- Frontend specs + - Support Chrome, Firefox, Safari + - Support mobile and desktop sizes + - All pages should be bookmarkable, including queries for charts and search + - Performance + - 2 seconds 95th pct for TTI + - Full page size must be under 500kb (including API calls) diff --git a/db/makefile b/db/makefile index b216814..2e0a8aa 100644 --- a/db/makefile +++ b/db/makefile @@ -15,12 +15,12 @@ start: .data -u $$UID:$$UID \ postgres:13-alpine -logs: - docker logs bodytrack-db - stop: @docker rm -f -v bodytrack-db +logs: + docker logs bodytrack-db + .PHONY: clean default start stop .data: diff --git a/db/schema/0000_root.sql b/db/schema/0000_root.sql index 3b96ee6..845eca8 100644 --- a/db/schema/0000_root.sql +++ b/db/schema/0000_root.sql @@ -1 +1,3 @@ CREATE TYPE UNIT AS ENUM ('Metric', 'Imperial'); + +CREATE TYPE LogType AS ENUM ('Weight', 'Height', 'Calories'); diff --git a/db/schema/0001_accounts.sql b/db/schema/0001_accounts.sql index 80c22cb..1881b7c 100644 --- a/db/schema/0001_accounts.sql +++ b/db/schema/0001_accounts.sql @@ -4,27 +4,27 @@ CREATE TABLE Accounts.Users ( id SERIAL PRIMARY KEY, email VARCHAR(128) NOT NULL UNIQUE, emailVerificationTs TIMESTAMP DEFAULT NULL, - passwordHash VARCHAR(512) NOT NULL + emailVerificationCode VARCHAR(128), + passwordHash VARCHAR(512) NOT NULL, + passwordResetCode VARCHAR(128) ); - - CREATE TABLE Accounts.Profile ( userId INT NOT NULL REFERENCES Accounts.Users(id), username VARCHAR(64) NOT NULL, birthdate TIMESTAMP NOT NULL, height DECIMAL NOT NULL, - displayUnit UNIT NOT NULL + displayUnit UNIT NOT NULL DEFAULT 'Metric' ); +CREATE INDEX profile_userid ON Accounts.Profile USING btree (userId); CREATE TABLE Accounts.UsersAuth ( userId INT NOT NULL REFERENCES Accounts.Users(id), tokenHash VARCHAR(512) NOT NULL, - device VARCHAR(64) NOT NULL, + deviceId VARCHAR(64) NOT NULL, createdTs TIMESTAMP DEFAULT NOW(), sourceIp VARCHAR(32) DEFAULT NULL ); - - +CREATE INDEX usersauth_userid ON Accounts.UsersAuth USING btree (userId); diff --git a/db/schema/0002_stats.sql b/db/schema/0002_stats.sql index 7839046..339263b 100644 --- a/db/schema/0002_stats.sql +++ b/db/schema/0002_stats.sql @@ -1,11 +1,22 @@ CREATE SCHEMA Stats; -CREATE TABLE Stats.WeightLog ( +CREATE TABLE Stats.Log ( id SERIAL PRIMARY KEY, userId INT NOT NULL REFERENCES Accounts.Users(id), + logType LogType NOT NULL DEFAULT 'Weight', value DECIMAL NOT NULL, recordedTs TIMESTAMP NOT NULL DEFAULT NOW() ); -CREATE INDEX weightlog_userId ON stats.weightlog USING btree (userId); +CREATE INDEX log_logType ON Stats.Log USING btree (logType); +CREATE TABLE Stats.LogGoal ( + id SERIAL PRIMARY KEY, + userId INT NOT NULL REFERENCES Accounts.Users(id), + logType LogType NOT NULL, + value DECIMAL NOT NULL, + startTs TIMESTAMP NOT NULL DEFAULT NOW(), + endTs TIMESTAMP NOT NULL DEFAULT NOW() +); + +CREATE INDEX log_userId ON Stats.LogGoal USING btree (userId); diff --git a/db/schema/9005_data.sql b/db/schema/9005_data.sql index 0315b53..c30ac60 100644 --- a/db/schema/9005_data.sql +++ b/db/schema/9005_data.sql @@ -1,15 +1,54 @@ INSERT INTO Accounts.Users(email, emailVerificationTs, passwordHash) VALUES ('adam@vdhsn.com', NOW(), 'JDJhJDA0JDlMdVRFek9SQi92RGxucXpYOVZkRWVSQXphR2R2VGZPZGJvYWNMeDhZM2NmUkFRaVFUYkpP'); - INSERT INTO Accounts.Users(email, emailVerificationTs, passwordHash) VALUES ('adam@example.com', NULL, 'JDJhJDA0JDlMdVRFek9SQi92RGxucXpYOVZkRWVSQXphR2R2VGZPZGJvYWNMeDhZM2NmUkFRaVFUYkpP'); INSERT INTO Accounts.Profile (userId, username, birthdate, height, displayUnit) - VALUES (1, 'adam', '1990-09-28 5:23:54', 182.88, 'Imperial'); + VALUES (1, 'adam', '1990-09-28 5:23:54', 77.1107, 'Imperial'); INSERT INTO Accounts.Profile (userId, username, birthdate, height, displayUnit) - VALUES (2, 'adam', '1990-09-28 5:23:54', 182.88, 'Imperial'); - - -INSERT INTO Stats.WeightLog (userId, value) VALUES (1, 77.1107); + VALUES (2, 'adam', '1990-09-28 5:23:54', 77.1107, 'Imperial'); + + +INSERT INTO Stats.Log (userId, logType, value, recordedTs) + VALUES (1, 'Weight', 77.1107, '2020-02-01 00:00:00'); +INSERT INTO Stats.Log (userId, logType, value, recordedTs) + VALUES (1, 'Weight', 76.1107, '2020-02-02 00:00:00'); +INSERT INTO Stats.Log (userId, logType, value, recordedTs) + VALUES (1, 'Weight', 78.1107, '2020-02-03 00:00:00'); +INSERT INTO Stats.Log (userId, logType, value, recordedTs) + VALUES (1, 'Weight', 79.1107, '2020-02-04 00:00:00'); +INSERT INTO Stats.Log (userId, logType, value, recordedTs) + VALUES (1, 'Weight', 77.1107, '2020-02-07 00:00:00'); +INSERT INTO Stats.Log (userId, logType, value, recordedTs) + VALUES (1, 'Weight', 76.1107, '2020-02-12 00:00:00'); +INSERT INTO Stats.Log (userId, logType, value, recordedTs) + VALUES (1, 'Weight', 75.1107, '2020-02-18 00:00:00'); +INSERT INTO Stats.Log (userId, logType, value, recordedTs) + VALUES (1, 'Weight', 73.1107, '2020-02-22 00:00:00'); +INSERT INTO Stats.Log (userId, logType, value, recordedTs) + VALUES (1, 'Weight', 72.1107, '2020-02-27 00:00:00'); +INSERT INTO Stats.Log (userId, logType, value, recordedTs) + VALUES (1, 'Weight', 74.1107, '2020-03-01 00:00:00'); +INSERT INTO Stats.Log (userId, logType, value, recordedTs) + VALUES (1, 'Weight', 76.1107, '2020-03-02 00:00:00'); +INSERT INTO Stats.Log (userId, logType, value, recordedTs) + VALUES (1, 'Weight', 78.1107, '2020-03-03 00:00:00'); +INSERT INTO Stats.Log (userId, logType, value, recordedTs) + VALUES (1, 'Weight', 79.1107, '2020-03-04 00:00:00'); +INSERT INTO Stats.Log (userId, logType, value, recordedTs) + VALUES (1, 'Weight', 77.1107, '2020-03-07 00:00:00'); +INSERT INTO Stats.Log (userId, logType, value, recordedTs) + VALUES (1, 'Weight', 76.1107, '2020-03-12 00:00:00'); +INSERT INTO Stats.Log (userId, logType, value, recordedTs) + VALUES (1, 'Weight', 75.1107, '2020-03-18 00:00:00'); +INSERT INTO Stats.Log (userId, logType, value, recordedTs) + VALUES (1, 'Weight', 73.1107, '2020-03-27 00:00:00'); +INSERT INTO Stats.Log (userId, logType, value, recordedTs) + VALUES (1, 'Weight', 72.1107, '2020-03-30 00:00:00'); +INSERT INTO Stats.Log (userId, logType, value, recordedTs) + VALUES (1, 'Weight', 71.1107, '2020-04-04 00:00:00'); + +INSERT INTO Stats.LogGoal (userId, logType, value, endTs, startTs) + VALUES (1, 'Weight', 70, '2020-06-01 00:00:00', '2020-01-01 00:00:00'); diff --git a/makefile b/makefile index 3dac836..621446e 100644 --- a/makefile +++ b/makefile @@ -4,7 +4,6 @@ start: start-client start-api start-db stop: stop-api stop-db - start-db: cd db && $(MAKE) start