Code Monkey home page Code Monkey logo

librefit's Introduction

LibreFit: Your OpenSource Calorie Tracker


GitHub Release Coverage

LibreFit is my hobby project that allows you to calculate your TDEE (Total Daily Energy Expenditure) based on your body metrics, create goals for weight loss or weight gain from that and encourages you to track your daily calorie intake. Based in your input, you will see easy to understand charts to keep on track with your goal - all that with privacy first in mind.

Introduction

LibreFit was created after tracking my caloric intake with some Excel and Google Sheets over a year. Out of boredom I decided to try a new stack I've never done anything productive with, I quite enjoyed the journey and thus this project was born.

Dependencies

  • Gradle 7.6
  • Quarkus 3.5.0
  • Quinoa 2.3.5
  • Kotlin 1.9.0
  • SvelteKit 2.5.2
  • Skeleton 2.9.0
  • Postgres 15.2
  • OpenAPI 3.0.3

Structure

The project is structured as a gradle monorepo.

librefit
|- librefit-api
|- librefit-service
|- librefit-web

The build order is: librefit-service -> librefit-api -> librefit-web.

librefit-api

A subpackage that ingests the OpenAPI spec genereated by Quarkus and produces JSDoc descriptions with a homebrew generator that follows absolutely no standards which I'm both ashamed and proud of.

To generate fresh code from the spec, use

./gradlew librefit-api:ApiCodegen

librefit-service

The service part of the application. For dev purpose, it expects a running Postgres instance on http://localhost:5432. If you have a working docker installation, run

docker-compose up -d

or provide an own instance.

To generate the latest OpenAPI spec, run

./gradlew librefit-service:buildDependents

This will only work with passing integration tests ;)

To start the service, run

./gradlew librefit-service:quarkusDev

For more details, see librefit-service/README.md.

librefit-web

By default, librefit-service handles vite to serve directly from quarkus. If you prefer to run the modules separately, set %dev.quarkus.quinoa=false in application.properties.

You can either provide a node installation yourself or let gradle handle it for you. Gradle will download a node 21.7.0 environment for this project to use.

Start dev server with gradle:

./gradlew librefit-web:npm_run_dev

Start dev server with local npm installation:

cd librefit-web && npm run dev

For more commands, please see librefit-web/package.json and also librefit-web/README.md.

Attribution

The avatars were created with hotpot: https://hotpot.ai/art-generator

Contribute

If you have any suggestions, impediments or things that absolutely annoy you, please refer to the issue tracker linked at the top of this document. When you are interested in participating in development, feel free to contact me.

librefit's People

Contributors

tohuwabohu-io avatar

Stargazers

Abbos Donaboyev avatar

Watchers

Kostas Georgiou avatar  avatar

librefit's Issues

user profile

As a logged in user

  • I want to change my mail address
  • I want to change my nickname
  • I want to choose a user avatar
  • I want to suspend/delete my user account

historic data dates are sorted ascending

Describe the bug
When navigating to /tracker/calories, sometimes the oldest entry is displayed on top with the newest one on the bottom. Sometimes even the most recent one is at the top, followed by the oldest to the newer ones in ascending order.

To Reproduce
Steps to reproduce the behavior:

  1. Log in
  2. On the dashboard 'Average distribution' section, click on 'Show history'
  3. See how the data is ordered

Expected behavior
The most recent entry should be on top with the rest in descending order.

Screenshots
If applicable, add screenshots to help explain your problem.

Desktop (please complete the following information):

  • Browser Firefox Version 123.0.1

Additional context
Maybe pagination would be useful especially when there's a year worth of data.

food library

Being able to define, search for and maybe share a pre-set of things I regularly eat would be convenient feature on top of adding raw calories.

This is just a brainstorm placeholder and not something I can go into blindly, it will require a lot of planning.

serve SvelteKit app on quarkus w/ quinoa

Is your feature request related to a problem? Please describe.
Currently I build and deploy two different packages resulting in four machines total running on fly. This has several implications. When suspended

  • node server bff needs to wake up before processing a request which takes ~300ms
  • quarkus needs to wake up before handling requests received from node server which takes ~300ms + some time as the JVM image is being used (see #75 for improvement)
  • overall it takes too long.

Also from a cost perspective, I run and maintain not one but two services and the setup is overkill for such a small application.

Describe the solution you'd like
Add the quarkus quinoa plugin: https://docs.quarkiverse.io/quarkus-quinoa/dev/index.html

  • don't change how the codebase is structured. The setup works fine for local development
  • during build, the bundled webapp should be put into the WEB-INF/ folder and served from there
  • in the end there should be just one image built and deployed instead of two

make wizard interactive

  • wizard result should be able to automatically set the goal
  • wizard should be re-evaluated each month with recent parameters (weight) to update the goal

add environments

  • add dev and prod environment to librefit-web
  • add dev and prod environment to librefit-service

review JWT implementation

  • check token on server side
  • maybe add an authentication db table with active sessions
  • check expiration date on client side
  • add refresh token endpoint

revise api integration

Current setup has a few strange problems.

After npm link, vite sometimes does not refresh the librefit-api bundles which contradicts a smooth development cycle.
IntelliJ has a known bug in regards to custom path aliases: (Link) that got re-introduced by regression.
Generating API files directly in the web module results in TypeScript ambiguous import errors.

Look into some alternatives, e.g. by publishing the generated sources into the NPM registry instead of linking them.

restructure main page

  • top bar should only be visible when logged in
  • show register and login links on main page
  • add some filler text
  • create and add logo
  • find a better default font

JWT rotation for rest calls

Describe the bug
Token refresh happens only on navigating routes. Staying in a page too long before interacting pops an error message upon trying to change data.

To Reproduce
Steps to reproduce the behavior:

  1. Login in
  2. Navigate to any page, e.g. /profile
  3. Wait until JWT expires
  4. Change profile data and click save

Expected behavior
The action on save should succeed when the JWT expired and the token should be rotated. No error message should be displayed in that case.

Desktop (please complete the following information):

  • Browser: Mozilla Firefox Version 123.0.1

replace axios with fetch

The default way to go with SvelteKit is using fetch, currently the generated API sources are based on typescript-axios. Luckily, a typescript-fetch generator exists.

add docker volumes

  • replace quarkus dev service with dedicated postgres config
  • provide and populate test data

improve error handling

  • add some kind of ValidationError to erroneous responses in librefit-service
  • revise login, registration and tdee calculation forms

Note: In SvelteKit, when an error occurs during a SSR API call (e.g. by processing a form), the default way to handle errors is by navigating to an error page. It would be better UX if a toast message popup appeared instead and that's easier to implement when moving the API call to the client part. (related: sveltekit issue 4394)

registration

  • add password validation
  • add confirmation mail service
  • terms and conditions should include terms and conditions

revisit persistence decisions

revise db relations

Data model is managed by hibernate, the relations and primary keys should be annotated accordingly.

e.g. CalorieTrackerEntry should have an ID / UserID composite key and map to the LibreUser table.

Provided test data insert statements should take the key values generated by hibernate to avoid duplicate keys.

Registration confirmation

Is your feature request related to a problem? Please describe.
To ensure the usage of valid mail addresses, a user should confirm the registration within 14 days by activating the account after a mail notification.

Describe the solution you'd like
Upon registration, an activation link should be generated and sent by e-mail. After 14 days, the link becomes inactive and the account should be locked. Any further login attempts should not be possible, but the option to send a new activation link should be presented.

Describe alternatives you've considered
I thought about Sign-In with Google, Microsoft, Apple, ... etc. but given that I'm not aware what exactly will happen with the data I rather have this isolated in my application.

Additional context
Please see a related issue: #91
An E-Mail as a Service platform is recommended. It's easy to land on a blacklist if I would send it myself.
E.g.

wrapper for generated API

  • provide an entry point in librefit-api that handles the call + then() and catch() blocks to provide cleaner code in librefit-web
  • change base path to start with /api/v1 oder something like that

endpoint authorization

  • anything apart from the login and register endpoints should require authorization
  • add JWT endpoint and require a valid token for each endpoint

service code cleanup

  • remove user creation at startup and add to import.sql
  • remove main() from Authentication.kt
  • add CalorieTrackerResource.listEntries (range) test
  • remove hashCode and equals and toString methods from JPA entities
  • remove obsolete Error mappers: NoResultException, UnmodifiedException, UnauthorizedException

E-Mails entered are not validated strictly enough

Describe the bug
Only valid E-Mails should be able to register. Currently it's possible to provide nonsensical values.

To Reproduce
Steps to reproduce the behavior:

  1. Go to 'Not signed up yet?' on the landing page.
  2. Enter any value that contains an '@' and is longer than 4 characters, e.g. ab@cd
  3. Confirm

Expected behavior
The input field should be marked as invalid and registration should be rejected.

Additional context
Validation must be performed in both frontend and backend. An E-Mail must contain a prefix, a '@' character and a domain part.

update README

  • update generic README.md in librefit-service
  • update README.md in project root
  • update generic README.md in librefit-web

forecast

  • display weight forecast based on given data and the wizard result

password encryption

  • LibreUser password should follow current security standards

Note: Maybe use the quarkus-security-jpa package

add multi project build script

  • add gradle config to bundle librefit-api, librefit-web and librefit-service
  • remove generate.sh from librefit-api and integrate into gradle build

fly.io evaluation

Some lessons were learnt. TODO.

Deployment

The Dockerfile is needed to build an Image during fly.io deployment. Maybe the second image build can be avoided with fly deploy -i <image>, it's mentioned here: https://news.ycombinator.com/item?id=30019194

Service

  • Read secrets and put it into the deployment, this should be straight-forward
  • enable compression for application/json
  • as mentioned in #73 for GraalVM another workaround for flyway is needed. Given how fly.io works having a native image would be a massive benefit as there is a significant startup time difference

Web
This actually makes my head hurt. Responses from POST requests cannot be decoded in the browser. The only difference I found was:

  1. Content-Encoding is set to "gzip" -> the fly.io proxy does that automatically as mentioned here: https://community.fly.io/t/content-encoding-gzip/4000 I found no way to turn that off reliably
  2. Sometimes the Transfer-Encoding is not set
  3. Sometimes the Content-Length is not set

Example response headers (after POST on /tracker/weight/create):

headers: Headers(7) { "content-encoding" → "gzip", "content-type" → "application/json;charset=UTF-8", date → "Mon, 11 Mar 2024 13:58:54 GMT", … }
<entries>
"content-encoding": "gzip"
"content-type": "application/json;charset=UTF-8"
date: "Mon, 11 Mar 2024 13:58:54 GMT"
"fly-request-id": "01HRPWYA27YNRT1F06MMT98KWY-fra"
server: "Fly/0637d260 (2024-03-07)"
via: "1.1 fly.io, 2 fly.io"
"x-firefox-spdy": "h2"

This results in TypeError: Decoding failed. with no useful information. The response itself contains status 201 and everything looks fine in the logs. It works when testing locally both in the IDE and using docker-compose with the production-ready images. I can only reproduce it when I activate compression for application/json on quarkus.

Workaround: Only evaluate the response status code and print an error if anything went wrong. Fetch the added data with a GET request. This affects

  1. the dashboard: POST /tracker/calories/create, POST /tracker/weight/create
  2. both the calorie and weight tracker pages
  3. the TDEE wizard

facelift

  • integrate tailwind css and go bonkers

faulty automatic deployment of librefit-web

The browser console shows various modules being blocked during runtime and some things in the app do not work. E.g. opening the user panel or navigating in general. This seems to happen at random.

Steps to reproduce

  • Run CD github action
  • Open the page
  • Navigate and open the dev tools

image

However, manual deployment with flyctl on my local machine works.

csv import for calorie tracker

  • make the existing CSV importer available trough the frontend
  • define required CSV structure and print errors accordingly
  • mark imported entries in the database and provide a rollback function

revisit client side error handling

  • sometimes the success messages pops up but on the server api (not necessarily the service) an error occurred
  • this suggests some data has changed when in reality, nothing happened

update librefit-api node module

Current setup is too complicated.

  • in librefit-service, openapi.yaml should be generated by quarkus as build step and put into a folder
  • in librefit-web, openapi.yaml should be used to generate API boilerplate

IAM integration

Is your feature request related to a problem? Please describe.
Because of the SPA refactoring in #87, login behaviour was reduced to a single issued login token that gets lost on refresh with a strict time limit.

Describe the solution you'd like
I'd like to stay logged in until I log out. Maybe an IAM tool like keycloak could be integrated. From a technical perspective, I need a solution that allows me to revoke tokens in case of compromised access.

Describe alternatives you've considered
Currently SmallRye JWT is in place but given how the setup looks like now the refresh token rotation can't be implemented anymore.
Another option could be Quarkus Form-based Authentication

fetch timeout

  • client currently does not possess any kind of request timeout handling

calorie tracker entries are unsorted after editing

Describe the bug
When editing a calorie tracker entry and other entries are present, sometimes the entries become rearranged. They should stay in the same order.

To Reproduce
Steps to reproduce the behavior:

  1. Go to dashboard
  2. Click on the 'edit' pencil of an existing entry
  3. Enter a value and save
  4. See error

Expected behavior
The entries should stay sorted by the date they were added.

Desktop (please complete the following information):

  • Browser: Firefox Version 123.0.1

daily dashboard

As a user, I want to track my progress on a daily basis in an overview.

release readiness

  • set up container registry
  • add release workflow
  • build and publish librefit-service and librefit-web docker images

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.