hackatime/config/routes.rb
Mahad Kalam e3456be187
goaaaal! (#985)
* Add goals

* Fix up some migrations

* Formatting

* Simplify migration

* Update test/controllers/settings_goals_controller_test.rb

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* Update test/controllers/settings_goals_controller_test.rb

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* Fix svelte-check issues, make CI less janky on dev

* svelte-check/fix tests

* Fix N+1s

* Formatting!

* More tests, fix anonymization and N+1

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-02-19 18:47:01 +00:00

318 lines
13 KiB
Ruby
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

class AdminLevelConstraint
def initialize(*require)
@require = require.map(&:to_s)
end
def matches?(request)
return false unless request.session[:user_id]
user = User.find_by(id: request.session[:user_id])
user && @require.include?(user.admin_level)
end
end
Rails.application.routes.draw do
# Redirect to localhost from 127.0.0.1 to use same IP address with Vite server
constraints(host: "127.0.0.1") do
get "(*path)", to: redirect { |params, req| "#{req.protocol}localhost:#{req.port}/#{params[:path]}" }
end
mount Rswag::Api::Engine => "/api-docs"
mount Rswag::Ui::Engine => "/api-docs"
use_doorkeeper do
controllers authorizations: "custom_doorkeeper/authorizations"
end
post "/oauth/applications/:id/rotate_secret", to: "doorkeeper/applications#rotate_secret", as: :rotate_secret_oauth_application
root "static_pages#index"
resources :extensions, only: [ :index ]
constraints AdminLevelConstraint.new(:superadmin) do
mount GoodJob::Engine => "good_job"
mount Flipper::UI.app(Flipper) => "flipper", as: :flipper
namespace :admin do
resources :admin_users, only: [ :index, :update ] do
collection do
get :search
end
end
resources :oauth_applications, only: [ :index, :show, :edit, :update ] do
member do
post :toggle_verified
post :rotate_secret
end
end
end
end
constraints AdminLevelConstraint.new(:superadmin, :admin, :viewer) do
namespace :admin do
get "timeline", to: "timeline#show", as: :timeline
get "timeline/search_users", to: "timeline#search_users"
get "timeline/leaderboard_users", to: "timeline#leaderboard_users"
resources :trust_level_audit_logs, only: [ :index, :show ]
resources :admin_api_keys, except: [ :edit, :update ]
resources :deletion_requests, only: [ :index, :show ] do
member do
post :approve
post :reject
end
end
end
get "/impersonate/:id", to: "sessions#impersonate", as: :impersonate_user
end
constraints AdminLevelConstraint.new(:superadmin) do
namespace :admin do
resources :permissions, only: [ :index, :update ]
end
end
get "/stop_impersonating", to: "sessions#stop_impersonating", as: :stop_impersonating
if Rails.env.development?
mount LetterOpenerWeb::Engine, at: "/letter_opener"
end
# Reveal health status on /up that returns 200 if the app boots with no exceptions, otherwise 500.
# Can be used by load balancers and uptime monitors to verify that the app is live.
get "up" => "rails/health#show", as: :rails_health_check
# Render dynamic PWA files from app/views/pwa/* (remember to link manifest in application.html.erb)
# get "manifest" => "rails/pwa#manifest", as: :pwa_manifest
# get "service-worker" => "rails/pwa#service_worker", as: :pwa_service_worker
resources :static_pages, only: [ :index ] do
collection do
get :project_durations
get :currently_hacking
get :currently_hacking_count
get :streak
# get :timeline # Removed: Old route for timeline
end
end
get "/signin", to: "static_pages#signin", as: :signin
# Auth routes
get "/auth/hca", to: "sessions#hca_new", as: :hca_auth
get "/auth/hca/callback", to: "sessions#hca_create"
get "/auth/slack", to: "sessions#slack_new", as: :slack_auth
get "/auth/slack/callback", to: "sessions#slack_create"
get "/auth/github", to: "sessions#github_new", as: :github_auth
get "/auth/github/callback", to: "sessions#github_create"
delete "/auth/github/unlink", to: "sessions#github_unlink", as: :github_unlink
post "/auth/email", to: "sessions#email", as: :email_auth
post "/auth/email/add", to: "sessions#add_email", as: :add_email_auth
delete "/auth/email/unlink", to: "sessions#unlink_email", as: :unlink_email_auth
get "/auth/token/:token", to: "sessions#token", as: :auth_token
get "/auth/close_window", to: "sessions#close_window", as: :close_window
delete "signout", to: "sessions#destroy", as: "signout"
resources :leaderboards, only: [ :index ] do
get :entries, on: :collection
end
# Docs routes
# Note: llms.txt and llms-full.txt are served as static files from public/
# Generate them with: rails docs:generate_llms
get "docs", to: "docs#index", as: :docs
get "docs/*path", to: "docs#show", as: :doc
# Nested under users for admin access
resources :users, only: [] do
get "settings", on: :member, to: "settings/profile#show"
patch "settings", on: :member, to: "settings/profile#update"
member do
patch :update_trust_level
end
resource :wakatime_mirrors, only: [ :create ]
resources :wakatime_mirrors, only: [ :destroy ]
end
get "my/projects", to: "my/project_repo_mappings#index", as: :my_projects
# Namespace for current user actions
get "my/settings", to: "settings/profile#show", as: :my_settings
patch "my/settings", to: "settings/profile#update"
get "my/settings/profile", to: "settings/profile#show", as: :my_settings_profile
patch "my/settings/profile", to: "settings/profile#update"
get "my/settings/integrations", to: "settings/integrations#show", as: :my_settings_integrations
patch "my/settings/integrations", to: "settings/integrations#update"
get "my/settings/access", to: "settings/access#show", as: :my_settings_access
patch "my/settings/access", to: "settings/access#update"
get "my/settings/goals", to: "settings/goals#show", as: :my_settings_goals
post "my/settings/goals", to: "settings/goals#create", as: :my_settings_goals_create
patch "my/settings/goals/:goal_id", to: "settings/goals#update", as: :my_settings_goal_update
delete "my/settings/goals/:goal_id", to: "settings/goals#destroy", as: :my_settings_goal_destroy
get "my/settings/badges", to: "settings/badges#show", as: :my_settings_badges
get "my/settings/data", to: "settings/data#show", as: :my_settings_data
get "my/settings/admin", to: "settings/admin#show", as: :my_settings_admin
post "my/settings/migrate_heartbeats", to: "settings/data#migrate_heartbeats", as: :my_settings_migrate_heartbeats
post "my/settings/rotate_api_key", to: "settings/access#rotate_api_key", as: :my_settings_rotate_api_key
namespace :my do
resources :heartbeat_imports, only: [ :create, :show ]
resources :project_repo_mappings, param: :project_name, only: [ :edit, :update ], constraints: { project_name: /.+/ } do
member do
patch :archive
patch :unarchive
end
end
# resource :mailing_address, only: [ :show, :edit ]
# get "mailroom", to: "mailroom#index"
resources :heartbeats, only: [] do
collection do
get :export
post :import
end
end
end
get "deletion", to: "deletion_requests#show", as: :deletion
post "deletion", to: "deletion_requests#create", as: :create_deletion
delete "deletion", to: "deletion_requests#cancel", as: :cancel_deletion
get "my/wakatime_setup", to: "users#wakatime_setup"
get "my/wakatime_setup/step-2", to: "users#wakatime_setup_step_2"
get "my/wakatime_setup/step-3", to: "users#wakatime_setup_step_3"
get "my/wakatime_setup/step-4", to: "users#wakatime_setup_step_4"
post "/sailors_log/slack/commands", to: "slack#create"
post "/timedump/slack/commands", to: "slack#create"
get "/hackatime/v1", to: redirect("/", status: 302) # some clients seem to link this as the user's dashboard instead of /api/v1/hackatime
# API routes
namespace :api do
# This is our own API don't worry about compatibility.
namespace :v1 do
get "leaderboard", to: "leaderboard#daily"
get "leaderboard/daily", to: "leaderboard#daily"
get "leaderboard/weekly", to: "leaderboard#weekly"
get "stats", to: "stats#show"
get "users/:username/stats", to: "stats#user_stats"
get "users/:username/heartbeats/spans", to: "stats#user_spans"
get "users/:username/trust_factor", to: "stats#trust_factor"
get "users/:username/projects", to: "stats#user_projects"
get "users/:username/project/:project_name", to: "stats#user_project"
get "users/:username/projects/details", to: "stats#user_projects_details"
get "users/lookup_email/:email", to: "users#lookup_email", constraints: { email: /[^\/]+/ }
get "users/lookup_slack_uid/:slack_uid", to: "users#lookup_slack_uid"
get "banned_users/counts", to: "stats#banned_users_counts"
# External service Slack OAuth integration
post "external/slack/oauth", to: "external_slack#create_user"
resources :ysws_programs, only: [ :index ] do
post :claim, on: :collection
end
namespace :my do
get "heartbeats/most_recent", to: "heartbeats#most_recent"
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"
get "api_keys", to: "api_keys#index"
end
end
# Admin-only API namespace
namespace :admin do
namespace :v1 do
get "check", to: "admin#check"
get "user/info", to: "admin#user_info"
get "user/info_batch", to: "admin#user_info_batch"
get "user/heartbeats", to: "admin#user_heartbeats"
get "user/heartbeat_values", to: "admin#user_heartbeat_values"
get "user/get_users_by_ip", to: "admin#get_users_by_ip"
get "user/get_users_by_machine", to: "admin#get_users_by_machine"
get "user/stats", to: "admin#user_stats"
get "user/projects", to: "admin#user_projects"
get "user/trust_logs", to: "admin#trust_logs"
get "banned_users", to: "admin#banned_users"
post "user/get_user_by_email", to: "admin#get_user_by_email"
post "user/search_fuzzy", to: "admin#search_users_fuzzy"
post "user/convict", to: "admin#user_convict"
# Admin API Keys management
resources :admin_api_keys, only: [ :index, :show, :create, :destroy ]
# Trust level audit logs
resources :trust_level_audit_logs, only: [ :index, :show ]
# Deletion requests
resources :deletion_requests, only: [ :index, :show ] do
member do
post :approve
post :reject
end
end
# Permissions management
resources :permissions, only: [ :index ] do
collection do
patch ":id", to: "permissions#update", as: :update
end
end
# Timeline
get "timeline", to: "timeline#show"
get "timeline/search_users", to: "timeline#search_users"
get "timeline/leaderboard_users", to: "timeline#leaderboard_users"
get "users/:id/visualization/quantized", to: "admin#visualization_quantized"
get "alts/candidates", to: "admin#alt_candidates"
get "alts/shared_machines", to: "admin#shared_machines"
get "users/active", to: "admin#active_users"
post "audit_logs/counts", to: "admin#audit_logs_counts"
end
end
# wakatime compatible summary
get "summary", to: "summary#index"
# Everything in this namespace conforms to wakatime.com's API.
namespace :hackatime do
namespace :v1 do
get "/", to: redirect("/", status: 302) # some clients seem to link this as the user's dashboard instead of /api/v1/hackatime
get "/users/:id/statusbar/today", to: "hackatime#status_bar_today"
post "/users/:id/heartbeats", to: "hackatime#push_heartbeats"
get "/users/current/stats/last_7_days", to: "hackatime#stats_last_7_days"
end
end
namespace :internal do
post "revoke", to: "revocations#create"
post "/can_i_have_a_magic_link_for/:id", to: "magic_links#create"
end
end
get "/@:username", to: "profiles#show", as: :profile, constraints: { username: /[A-Za-z0-9_-]+/ }
get "/@:username/time_stats", to: "profiles#time_stats", as: :profile_time_stats, constraints: { username: /[A-Za-z0-9_-]+/ }
get "/@:username/projects", to: "profiles#projects", as: :profile_projects, constraints: { username: /[A-Za-z0-9_-]+/ }
get "/@:username/languages", to: "profiles#languages", as: :profile_languages, constraints: { username: /[A-Za-z0-9_-]+/ }
get "/@:username/editors", to: "profiles#editors", as: :profile_editors, constraints: { username: /[A-Za-z0-9_-]+/ }
get "/@:username/activity", to: "profiles#activity", as: :profile_activity, constraints: { username: /[A-Za-z0-9_-]+/ }
# SEO routes
get "/sitemap.xml", to: "sitemap#sitemap", defaults: { format: "xml" }
get "/wakatime-alternative", to: "static_pages#wakatime_alternative"
# fuck ups
match "/400", to: "errors#bad_request", via: :all
match "/404", to: "errors#not_found", via: :all
match "/422", to: "errors#unprocessable_entity", via: :all
match "/500", to: "errors#internal_server_error", via: :all
end