mirror of
https://github.com/System-End/hackatime.git
synced 2026-04-19 19:55:16 +00:00
Patch up oauth implementation (#560)
This commit is contained in:
parent
15744b3442
commit
5ae07f5643
10 changed files with 156 additions and 1 deletions
|
|
@ -0,0 +1,30 @@
|
|||
module Api
|
||||
module V1
|
||||
module Authenticated
|
||||
class HeartbeatsController < ApplicationController
|
||||
def latest
|
||||
heartbeat = current_user.heartbeats
|
||||
.where.not(source_type: :test_entry)
|
||||
.order(time: :desc)
|
||||
.first
|
||||
|
||||
if heartbeat
|
||||
render json: {
|
||||
id: heartbeat.id,
|
||||
created_at: heartbeat.created_at,
|
||||
project: heartbeat.project,
|
||||
language: heartbeat.language,
|
||||
editor: heartbeat.editor,
|
||||
operating_system: heartbeat.operating_system,
|
||||
machine: heartbeat.machine,
|
||||
file: heartbeat.file,
|
||||
duration: heartbeat.duration
|
||||
}
|
||||
else
|
||||
render json: { heartbeat: nil }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
22
app/controllers/api/v1/authenticated/hours_controller.rb
Normal file
22
app/controllers/api/v1/authenticated/hours_controller.rb
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
module Api
|
||||
module V1
|
||||
module Authenticated
|
||||
class HoursController < ApplicationController
|
||||
def index
|
||||
start_date = params[:start_date]&.to_date || 7.days.ago.to_date
|
||||
end_date = params[:end_date]&.to_date || Date.current
|
||||
|
||||
total_seconds = current_user.heartbeats
|
||||
.where(created_at: start_date.beginning_of_day..end_date.end_of_day)
|
||||
.duration_seconds
|
||||
|
||||
render json: {
|
||||
start_date: start_date,
|
||||
end_date: end_date,
|
||||
total_seconds: total_seconds
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
47
app/controllers/api/v1/authenticated/projects_controller.rb
Normal file
47
app/controllers/api/v1/authenticated/projects_controller.rb
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
module Api
|
||||
module V1
|
||||
module Authenticated
|
||||
class ProjectsController < ApplicationController
|
||||
def index
|
||||
projects = current_user.heartbeats
|
||||
.where.not(project: [ nil, "" ])
|
||||
.group(:project)
|
||||
.map { |project|
|
||||
{
|
||||
name: project,
|
||||
total_seconds: time_per_project[project] || 0,
|
||||
most_recent_heartbeat: most_recent_heartbeat_per_project[project] ? Time.at(most_recent_heartbeat_per_project[project]).strftime("%Y-%m-%dT%H:%M:%SZ") : nil,
|
||||
percentage: time_per_project.sum { |_, secs| secs }.zero? ? 0 : ((time_per_project[project] || 0) / time_per_project.sum { |_, secs| secs }.to_f * 100).round(2),
|
||||
repo: project_repo_mappings[project]&.repo
|
||||
}
|
||||
}
|
||||
|
||||
render json: { projects: projects }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def project_repo_mappings
|
||||
@project_repo_mappings ||= current_user.project_repo_mappings
|
||||
.index_by(&:project)
|
||||
end
|
||||
|
||||
def time_per_project
|
||||
@time_per_project ||= current_user.heartbeats
|
||||
.with_valid_timestamps
|
||||
.where.not(project: [ nil, "" ])
|
||||
.group(:project)
|
||||
.duration_seconds
|
||||
end
|
||||
|
||||
def most_recent_heartbeat_per_project
|
||||
@most_recent_heartbeat_per_project ||= current_user.heartbeats
|
||||
.with_valid_timestamps
|
||||
.where.not(project: [ nil, "" ])
|
||||
.group(:project)
|
||||
.maximum(:time)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
13
app/controllers/api/v1/authenticated/streak_controller.rb
Normal file
13
app/controllers/api/v1/authenticated/streak_controller.rb
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
module Api
|
||||
module V1
|
||||
module Authenticated
|
||||
class StreakController < ApplicationController
|
||||
def show
|
||||
render json: {
|
||||
streak_days: current_user.streak_days
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -108,6 +108,16 @@ class User < ApplicationRecord
|
|||
has_many :trust_level_audit_logs, dependent: :destroy
|
||||
has_many :trust_level_changes_made, class_name: "TrustLevelAuditLog", foreign_key: "changed_by_id", dependent: :destroy
|
||||
|
||||
has_many :access_grants,
|
||||
class_name: "Doorkeeper::AccessGrant",
|
||||
foreign_key: :resource_owner_id,
|
||||
dependent: :delete_all
|
||||
|
||||
has_many :access_tokens,
|
||||
class_name: "Doorkeeper::AccessToken",
|
||||
foreign_key: :resource_owner_id,
|
||||
dependent: :delete_all
|
||||
|
||||
def streak_days
|
||||
@streak_days ||= heartbeats.daily_streaks_for_users([ id ]).values.first
|
||||
end
|
||||
|
|
|
|||
|
|
@ -3,6 +3,10 @@
|
|||
Doorkeeper.configure do
|
||||
base_controller "ApplicationController"
|
||||
|
||||
default_scopes "profile"
|
||||
optional_scopes "read"
|
||||
enforce_configured_scopes
|
||||
|
||||
resource_owner_authenticator do
|
||||
current_user || redirect_to(minimal_login_path(continue: request.fullpath))
|
||||
end
|
||||
|
|
@ -20,4 +24,11 @@ Doorkeeper.configure do
|
|||
access_token_expires_in 16.years
|
||||
|
||||
reuse_access_token
|
||||
|
||||
# Allow public clients (desktop/mobile apps) without client secrets
|
||||
allow_blank_redirect_uri
|
||||
skip_client_authentication_for_password_grant
|
||||
|
||||
# Enable PKCE for public clients
|
||||
force_ssl_in_redirect_uri false
|
||||
end
|
||||
|
|
|
|||
|
|
@ -154,8 +154,14 @@ Rails.application.routes.draw do
|
|||
get "heartbeats", to: "heartbeats#index"
|
||||
end
|
||||
|
||||
# oauth authenticated namespace
|
||||
namespace :authenticated do
|
||||
resources :me, only: [ :index ]
|
||||
get "hours", to: "hours#index"
|
||||
get "streak", to: "streak#show"
|
||||
get "projects", to: "projects#index"
|
||||
# get "projects/:name", to: "projects#show", constraints: { name: /.+/ }
|
||||
get "heartbeats/latest", to: "heartbeats#latest"
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
class AddForeignKeysToOauthAccess < ActiveRecord::Migration[8.0]
|
||||
def change
|
||||
add_foreign_key :oauth_access_grants, :users, column: :resource_owner_id
|
||||
add_foreign_key :oauth_access_tokens, :users, column: :resource_owner_id
|
||||
end
|
||||
end
|
||||
4
db/schema.rb
generated
4
db/schema.rb
generated
|
|
@ -10,7 +10,7 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema[8.0].define(version: 2025_08_21_021751) do
|
||||
ActiveRecord::Schema[8.0].define(version: 2025_10_03_161836) do
|
||||
create_schema "pganalyze"
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
|
|
@ -584,7 +584,9 @@ ActiveRecord::Schema[8.0].define(version: 2025_08_21_021751) do
|
|||
add_foreign_key "leaderboard_entries", "users"
|
||||
add_foreign_key "mailing_addresses", "users"
|
||||
add_foreign_key "oauth_access_grants", "oauth_applications", column: "application_id"
|
||||
add_foreign_key "oauth_access_grants", "users", column: "resource_owner_id"
|
||||
add_foreign_key "oauth_access_tokens", "oauth_applications", column: "application_id"
|
||||
add_foreign_key "oauth_access_tokens", "users", column: "resource_owner_id"
|
||||
add_foreign_key "physical_mails", "users"
|
||||
add_foreign_key "project_repo_mappings", "repositories"
|
||||
add_foreign_key "project_repo_mappings", "users"
|
||||
|
|
|
|||
|
|
@ -2,6 +2,14 @@
|
|||
# development, test). The code here should be idempotent so that it can be executed at any point in every environment.
|
||||
# The data can then be loaded with the bin/rails db:seed command (or created alongside the database with db:setup).
|
||||
|
||||
Doorkeeper::Application.find_or_create_by(
|
||||
name: "Hackatime Desktop",
|
||||
redirect_uri: "hackatime://auth/callback",
|
||||
uid: "BPr5VekIV-xuQ2ZhmxbGaahJ3XVd7gM83pql-HYGYxQ",
|
||||
scopes: [ "profile" ],
|
||||
confidential: false,
|
||||
)
|
||||
|
||||
# Only seed test data in development environment
|
||||
if Rails.env.development?
|
||||
# Creating test user
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue