Compare commits

...

13 Commits

Author SHA1 Message Date
Arthur POULET 35b69ef33a
Loosen on dependencies version
continuous-integration/drone/push Build is passing Details
2022-07-28 00:47:13 +02:00
Arthur POULET 46d14c2917
Update dependencies (security)
continuous-integration/drone/push Build is failing Details
continuous-integration/drone/tag Build is failing Details
2022-07-28 00:14:35 +02:00
Arthur POULET 3d607ade56 deps: execute bundle update 2022-06-20 23:48:15 +02:00
Arthur POULET 08a2729ce6
Merge remote-tracking branch 'origin/develop' into develop
continuous-integration/drone/push Build is passing Details
2022-04-23 11:30:35 +02:00
Arthur POULET d4de167df2
Merge remote-tracking branch 'origin/master'
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2022-04-23 11:28:44 +02:00
Arthur POULET 29e78b7f52
cookies: add a duration for cookies 2022-04-23 11:27:55 +02:00
Arthur POULET 5f8afb008e drone: update ruby image
continuous-integration/drone/push Build is passing Details
2022-03-27 17:27:39 +02:00
Arthur POULET b9bc73b3b8 recalls: add a red pill to remind recalls to do
closes #61
2022-03-27 17:23:31 +02:00
Arthur POULET 8efe5d3ab3
offset: fix offset generator for yesterday 2022-03-19 11:13:47 +01:00
Arthur POULET db9c5cd7a4 prefs: add show full date checkbox
* factorize user prefs cookies
* add checkboxe
2022-02-28 23:47:14 +01:00
Arthur POULET 76da1fd1a8 systems: fix UserHelper inclusion 2022-02-28 22:56:26 +01:00
Arthur POULET 1f44afb893 layout: add the full date (today/yesterday) 2022-02-28 22:52:57 +01:00
Arthur POULET 0485f8131d
prefs: add offset as a user pref 2022-02-26 16:26:46 +01:00
14 changed files with 199 additions and 104 deletions

View File

@ -3,7 +3,7 @@ name: default
steps:
- name: test
image: ruby:3.0
image: ruby:3.1
environment:
LIFEPEX_DB: "sqlite://test.db"
LIFEPEX_ENV: "test"

28
Gemfile
View File

@ -3,34 +3,34 @@
source "https://rubygems.org"
# web
gem "puma", "~> 5.3"
gem "sinatra", "~> 2.1"
gem "sinatra-contrib", "~> 2.1"
gem "slim", "~> 4.1"
gem "puma", "~> 5"
gem "sinatra", "~> 2"
gem "sinatra-contrib", "~> 2"
gem "slim", "~> 4"
# database
gem "sequel", "~> 5.43"
gem "sequel", "~> 5"
# you # comment the drivers you don't want
gem "sqlite3", "~> 1.4"
gem "sqlite3", "~> 1"
# gem "pg", "~> 1.2"
# security
gem "jwt", "~> 2.2"
gem "bcrypt", "~> 3.1"
gem "rack_csrf", "~> 2.6"
gem "jwt", "~> 2"
gem "bcrypt", "~> 3"
gem "rack_csrf", "~> 2"
# api tools
gem "doc_my_routes"
# debug and helpers
gem "colorize", "~> 0.8.1"
gem "activesupport", "= 6.1.3.1"
gem "colorize", "~> 0.8"
gem "activesupport", "~> 6"
# tests
group :test do
gem "pry", "~> 0.14.1"
gem "rack-test", "~> 1.1", require: false
gem "pry"
gem "rack-test", "~> 1", require: false
# gem "simplecov", "~> 0.21.2", require: false
end
gem "dotenv", "~> 2.7"
gem "dotenv", "~> 2"

View File

@ -1,83 +1,81 @@
GEM
remote: https://rubygems.org/
specs:
activesupport (6.1.3.1)
activesupport (6.1.6.1)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 1.6, < 2)
minitest (>= 5.1)
tzinfo (~> 2.0)
zeitwerk (~> 2.3)
bcrypt (3.1.16)
bcrypt (3.1.18)
coderay (1.1.3)
colorize (0.8.1)
concurrent-ruby (1.1.9)
concurrent-ruby (1.1.10)
doc_my_routes (0.13.0)
dotenv (2.7.6)
i18n (1.8.10)
dotenv (2.8.1)
i18n (1.12.0)
concurrent-ruby (~> 1.0)
jwt (2.2.3)
jwt (2.4.1)
method_source (1.0.0)
minitest (5.14.4)
minitest (5.16.2)
multi_json (1.15.0)
mustermann (1.1.1)
mustermann (2.0.2)
ruby2_keywords (~> 0.0.1)
nio4r (2.5.7)
pg (1.2.3)
nio4r (2.5.8)
pry (0.14.1)
coderay (~> 1.1)
method_source (~> 1.0)
puma (5.3.2)
puma (5.6.4)
nio4r (~> 2.0)
rack (2.2.3)
rack-protection (2.1.0)
rack (2.2.4)
rack-protection (2.2.2)
rack
rack-test (1.1.0)
rack (>= 1.0, < 3)
rack_csrf (2.6.0)
rack (>= 1.1.0)
ruby2_keywords (0.0.4)
sequel (5.45.0)
sinatra (2.1.0)
mustermann (~> 1.0)
ruby2_keywords (0.0.5)
sequel (5.58.0)
sinatra (2.2.2)
mustermann (~> 2.0)
rack (~> 2.2)
rack-protection (= 2.1.0)
rack-protection (= 2.2.2)
tilt (~> 2.0)
sinatra-contrib (2.1.0)
sinatra-contrib (2.2.2)
multi_json
mustermann (~> 1.0)
rack-protection (= 2.1.0)
sinatra (= 2.1.0)
mustermann (~> 2.0)
rack-protection (= 2.2.2)
sinatra (= 2.2.2)
tilt (~> 2.0)
slim (4.1.0)
temple (>= 0.7.6, < 0.9)
tilt (>= 2.0.6, < 2.1)
sqlite3 (1.4.2)
sqlite3 (1.4.4)
temple (0.8.2)
tilt (2.0.10)
tzinfo (2.0.4)
tilt (2.0.11)
tzinfo (2.0.5)
concurrent-ruby (~> 1.0)
zeitwerk (2.4.2)
zeitwerk (2.6.0)
PLATFORMS
x86_64-linux
DEPENDENCIES
activesupport (= 6.1.3.1)
bcrypt (~> 3.1)
colorize (~> 0.8.1)
activesupport (~> 6)
bcrypt (~> 3)
colorize (~> 0.8)
doc_my_routes
dotenv (~> 2.7)
jwt (~> 2.2)
pg (~> 1.2)
pry (~> 0.14.1)
puma (~> 5.3)
rack-test (~> 1.1)
rack_csrf (~> 2.6)
sequel (~> 5.43)
sinatra (~> 2.1)
sinatra-contrib (~> 2.1)
slim (~> 4.1)
sqlite3 (~> 1.4)
dotenv (~> 2)
jwt (~> 2)
pry
puma (~> 5)
rack-test (~> 1)
rack_csrf (~> 2)
sequel (~> 5)
sinatra (~> 2)
sinatra-contrib (~> 2)
slim (~> 4)
sqlite3 (~> 1)
BUNDLED WITH
2.2.16

View File

@ -95,3 +95,9 @@ LIFEPEX_ENV=test rake db:migrate
```
Then if you want to run the test, simply type `rake test` (you will need the startup env variable to be set first).
### Debug
Some debug options can be enabled with the env variable
`LIFEPEX_ENV=debug`

View File

@ -70,6 +70,10 @@ h2 {
height: 45px;
}
.smaller {
font-size: x-small !important;
}
.highcharts-figure, .highcharts-data-table table {
min-width: 360px;
max-width: 800px;

View File

@ -6,4 +6,18 @@ class LifePex::User < Sequel::Model(:users)
def password=(clear_password)
self.hashed_password = BCrypt::Password.create(clear_password)
end
def recalls_not_validated(cached: true)
@recalls_not_validated = nil if !cached
pexs = LifePex::Pex.setup_user_pexs(user_id: id, user_pexs: user_pexs)
@recalls_not_validated ||= recalls.filter do |recall|
pex = pexs[recall[:pex_id]]
validated = pex[:count_by_date].filter do |date, _|
date >= Date.today - recall[:span_duration]
end.values.sum
validated < recall[:repeated]
end
end
end

View File

@ -1,11 +1,33 @@
class LifePex::Systems::AuthSystem < Sinatra::Base
helpers Sinatra::Cookies
include JSON::API
include LifePex::UsersHelper
def setup_user_cookie!(user_id)
response.set_cookie(
"auth",
{
value: JWT.encode({ "user_id" => user_id }, LifePex::SECRET),
expires: Time.now + 2.days,
path: "/",
})
end
def renew_user_cookie!
response.set_cookie(
"auth",
{
value: cookies["auth"],
expires: Time.now + 2.days,
path: "/",
})
end
def user_id_decoded(cookies = nil)
cookies = cookies() if cookies.nil?
begin
decoded = JWT.decode(cookies["auth"], LifePex::SECRET)
renew_user_cookie!
decoded[0]["user_id"]
rescue => err
STDERR.puts "user_id_decoded: #{err}"

View File

@ -1,32 +1,14 @@
require "date"
require "active_support/all"
require_relative "./auth.rb"
require_relative "./csrf.rb"
require_relative "../utils/users.rb"
class LifePex::Systems::PexSystem < LifePex::Systems::AuthSystem
include JSON::API
include LifePex::Systems::CrlfHelper
def my_user_pexs(cookies, date = nil)
params = {
user_id: user_id_decoded(cookies),
}
params[:created_at] = date unless date.nil?
LifePex::UserPex.where(params).all()
end
def get_user_date
date = cookies["date"]
if date && Date.respond_to?(date)
Date.send date
elsif date == "yesterday"
Date.today - 1
else
Date.today
end
end
get "/today", auth: [] do
cookies.set "date", { value: "today", httponly: false }
cookies.set "date", { value: "now", httponly: false }
redirect "/"
end

View File

@ -3,7 +3,6 @@ require_relative "./auth.rb"
require_relative "./csrf.rb"
class LifePex::Systems::UserSystem < LifePex::Systems::AuthSystem
include JSON::API
include LifePex::Systems::CrlfHelper
DEFAULT_PEXS_FOR_NEW_USERS = YAML.load_file "config/default_pexs_for_new_users.yaml"
@ -12,14 +11,10 @@ class LifePex::Systems::UserSystem < LifePex::Systems::AuthSystem
slim :login
end
def setup_user_cookie!(user_id)
cookies["auth"] = JWT.encode({ "user_id" => user_id }, LifePex::SECRET)
end
def login(params)
user = LifePex::User.where(username: params["username"]).first
if user && BCrypt::Password.new(user[:hashed_password]) == params["password"]
cookies["auth"] = JWT.encode({ "user_id" => user[:id] }, LifePex::SECRET)
setup_user_cookie!(user[:id])
user
else
nil
@ -28,7 +23,6 @@ class LifePex::Systems::UserSystem < LifePex::Systems::AuthSystem
post "/login", provides: 'html' do
if user = login(params)
cookies["auth"] = JWT.encode({ "user_id" => user[:id] }, LifePex::SECRET)
redirect "/"
else
slim :login, locals: { flash: { danger: "Failed to login" } }
@ -93,10 +87,29 @@ class LifePex::Systems::UserSystem < LifePex::Systems::AuthSystem
show_preferences
end
UserPreferenceCookie = Struct.new(:cookie, :allow_blank, :convert, :html, :description)
USER_PREFERENCES = {
"inputRecapDays" => UserPreferenceCookie.new("recap_days", false, :to_i, { type: "text" }, "Amount of days to show in the recap"),
"inputLateDayOffset" => UserPreferenceCookie.new("late_day_offset", false, :to_f, { type: "number", min: "0", max: "24", step: "0.1" }, "Offset for today (so a few hours after midnight is still today)"),
"inputShowFullDate" => UserPreferenceCookie.new("show_full_date", true, :to_s, { type: "checkbox" }, "Show or hide the date of the current tab"),
}
post "/preferences" do
if !(recap_days = params["inputRecapDays"]).blank?
cookies["recap_days"] = recap_days.to_i
USER_PREFERENCES.each do |param_name, upc|
current_param = params[param_name]
next if !upc.allow_blank && current_param.blank?
cookies[upc.cookie] =
case upc.convert
when Symbol
current_param.send(upc.convert)
when Proc
upc.convert.call(current_param)
else
current_param
end
end
show_preferences
end

View File

@ -3,7 +3,6 @@ require_relative "./api_response"
require "csv"
class LifePex::Systems::UserPexSystem < LifePex::Systems::AuthSystem
include JSON::API
include LifePex::Systems::ApiResponse
set :protection, :except => [:frame_options, :json_csrf]
@ -134,5 +133,6 @@ class LifePex::Systems::UserPexSystem < LifePex::Systems::AuthSystem
end
end
include LifePex::Systems::ApiList
end

View File

@ -1,3 +1,5 @@
require "active_support/all"
module JSON::API
def json_params
begin
@ -10,18 +12,26 @@ module JSON::API
end
end
def date_input_convertor(date)
if date && Date.respond_to?(date)
Date.send date
elsif date == "yesterday"
Date.today - 1
else
begin
Date.parse date
rescue => _
Date.today
DATE_GENERATOR = {
"yesterday" => ->() { DateTime.now - 24.hours },
"now" => ->() { DateTime.now },
"today" => ->() { DateTime.now },
nil => ->() { DateTime.now },
}
# @param [String] date: either a date iso formatted or a word to be sent to DateTime
# @param [Float] offset: an amount of hours to remove from the date, useful for setting the start of the day hours after midnight
def date_input_convertor(date = "now", offset = 0.0)
base_time =
if DATE_GENERATOR.key?(date)
DATE_GENERATOR[date].call()
else
begin
DateTime.parse date
rescue => _
DateTime.now
end
end
end
(base_time - offset.hours).to_date
end
# params:

15
src/utils/users.rb Normal file
View File

@ -0,0 +1,15 @@
module LifePex::UsersHelper
def my_user_pexs(cookies, date = nil)
params = {
user_id: user_id_decoded(cookies),
}
params[:created_at] = date unless date.nil?
LifePex::UserPex.where(params).all()
end
def get_user_date
date = cookies["date"]
offset = cookies["late_day_offset"].to_f
date_input_convertor(date, offset)
end
end

View File

@ -20,9 +20,13 @@ html lang="en"
- if cookies["date"] == "yesterday"
a.navbar-brand href="/"
| Yesterday
- if cookies["show_full_date"] == "on"
.smaller= get_user_date
- else
a.navbar-brand href="/"
| Today
- if cookies["show_full_date"] == "on"
.smaller= get_user_date
button.navbar-toggler type="button" data-bs-toggle="collapse" data-bs-target="#navbar-collapser" aria-controls="navbar-collapser" aria-expanded="false" aria-label="Toggle navigation"
span.navbar-toggler-icon/
.collapse.navbar-collapse#navbar-collapser
@ -35,7 +39,11 @@ html lang="en"
li.nav-item
a.btn.btn-lg.btn-dark href="/yesterday" Yesterday
li.nav-item
a.btn.btn-lg.btn-dark href="/recap" Recap
a.btn.btn-lg.btn-dark.position-relative href="/recap"
| Recap
- if (recalls_count = current_user.recalls_not_validated.count) > 0
span.position-absolute.top-0.start-100.translate-middle.badge.rounded-pill.bg-danger
= recalls_count
li.nav-item
a.btn.btn-lg.btn-dark href="/achievements" Achievements
li.nav-item

View File

@ -6,14 +6,37 @@
| Recap
form(method="POST")
== csrf_tag
.form-group.row
label.col-sm-12.col-form-label for="inputRecapDays"
strong
| Days to show by default
input#inputUsername.form-control.form-control-lg name="inputRecapDays" type="text" value=cookies["recap_days"] /
/-
- USER_PREFERENCES.each do |param_name, upc|
- if upc.html[:type] == "checkbox"
.form-group.form-check.form-switch
label.col-sm-12.form-check-label for=param_name
strong=upc.description
- if cookies[upc.cookie] == "on"
input.form-check-input *upc.html name=param_name checked="on" /
- else
input.form-check-input *upc.html name=param_name /
- else
.form-group.row
label.col-sm-12.col-form-label for=param_name
strong=upc.description
input.form-control.form-control-lg *upc.html name=param_name value=cookies[upc.cookie] /
.form-group.row
input.btn.btn-lg.btn-block type="submit" value="Update"
- if LifePex::APP_ENV == "debug"
h2
| Debug
p
table
- cookies.each do |k, v|
tr
td= "cookies.#{k}"
td= v
tr
td
| get_user_date
td= get_user_date
h2
| Private data export