From 4091ac637f504c20be2d8f8aa2ab4f5d330bfd1c Mon Sep 17 00:00:00 2001
From: Zach Latta
Date: Wed, 28 May 2025 17:15:22 -0400
Subject: [PATCH] Add /docs section
---
Gemfile | 3 +
Gemfile.lock | 2 +
TODO.md | 22 ++++++
app/controllers/docs_controller.rb | 109 ++++++++++++++++++++++++++
app/views/docs/index.html.erb | 53 +++++++++++++
app/views/docs/show.html.erb | 30 +++++++
app/views/shared/_nav.html.erb | 5 ++
config/routes.rb | 4 +
docs/api/authentication.md | 102 ++++++++++++++++++++++++
docs/api/overview.md | 49 ++++++++++++
docs/getting-started/configuration.md | 76 ++++++++++++++++++
docs/getting-started/installation.md | 59 ++++++++++++++
docs/getting-started/quick-start.md | 48 ++++++++++++
docs/index.md | 28 +++++++
public/404.html | 10 +--
public/robots.txt | 17 +++-
16 files changed, 606 insertions(+), 11 deletions(-)
create mode 100644 TODO.md
create mode 100644 app/controllers/docs_controller.rb
create mode 100644 app/views/docs/index.html.erb
create mode 100644 app/views/docs/show.html.erb
create mode 100644 docs/api/authentication.md
create mode 100644 docs/api/overview.md
create mode 100644 docs/getting-started/configuration.md
create mode 100644 docs/getting-started/installation.md
create mode 100644 docs/getting-started/quick-start.md
create mode 100644 docs/index.md
diff --git a/Gemfile b/Gemfile
index 05bef0a..739a042 100644
--- a/Gemfile
+++ b/Gemfile
@@ -91,6 +91,9 @@ gem "norairrecord", "~> 0.3.0"
# Country codes
gem "countries"
+# Markdown parsing
+gem "redcarpet"
+
group :development, :test do
# See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem
gem "debug", platforms: %i[ mri windows ], require: "debug/prelude"
diff --git a/Gemfile.lock b/Gemfile.lock
index 09a18c1..cb3413e 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -380,6 +380,7 @@ GEM
rdoc (6.14.0)
erb
psych (>= 4.0.0)
+ redcarpet (3.6.1)
regexp_parser (2.10.0)
reline (0.6.1)
io-console (~> 0.5)
@@ -544,6 +545,7 @@ DEPENDENCIES
rack-cors
rack-mini-profiler
rails (~> 8.0.2)
+ redcarpet
rubocop-rails-omakase
selenium-webdriver
sentry-rails
diff --git a/TODO.md b/TODO.md
new file mode 100644
index 0000000..1e72d5b
--- /dev/null
+++ b/TODO.md
@@ -0,0 +1,22 @@
+# Documentation System Implementation
+
+## Tasks:
+- [x] Create docs controller with SEO-friendly routes
+- [x] Create docs views with breadcrumbs and proper styling
+- [x] Add markdown parsing capability (using redcarpet gem)
+- [x] Create docs directory structure for markdown files
+- [x] Add docs navigation link to sidebar
+- [x] Ensure docs are accessible without login
+- [x] Create sample documentation files
+- [x] Style docs pages to match existing site CSS
+- [x] Test the implementation
+
+## Documentation System Complete! ✅
+
+The docs system is now fully implemented with:
+- SEO-friendly URLs (/docs, /docs/path/to/page)
+- Markdown file support with proper parsing
+- Breadcrumb navigation
+- Responsive design matching the app's style
+- Public access (no login required)
+- Sample documentation covering getting started, API, etc.
diff --git a/app/controllers/docs_controller.rb b/app/controllers/docs_controller.rb
new file mode 100644
index 0000000..8f5fb98
--- /dev/null
+++ b/app/controllers/docs_controller.rb
@@ -0,0 +1,109 @@
+class DocsController < ApplicationController
+ # Docs are publicly accessible - no authentication required
+
+ def index
+ @docs = docs_structure
+ end
+
+ def show
+ @doc_path = params[:path] || "index"
+ @breadcrumbs = build_breadcrumbs(@doc_path)
+
+ file_path = Rails.root.join("docs", "#{@doc_path}.md")
+
+ unless File.exist?(file_path)
+ # Try with index.md in the directory
+ dir_path = Rails.root.join("docs", @doc_path, "index.md")
+ if File.exist?(dir_path)
+ file_path = dir_path
+ else
+ render_not_found and return
+ end
+ end
+
+ @content = File.read(file_path)
+ @title = extract_title(@content) || @doc_path.humanize
+ @rendered_content = render_markdown(@content)
+ rescue => e
+ Rails.logger.error "Error loading docs: #{e.message}"
+ render_not_found
+ end
+
+ private
+
+ def docs_structure
+ docs_dir = Rails.root.join("docs")
+ return {} unless Dir.exist?(docs_dir)
+
+ structure = {}
+ Dir.glob("#{docs_dir}/**/*.md").each do |file|
+ relative_path = Pathname.new(file).relative_path_from(docs_dir).to_s
+ path_parts = relative_path.sub(/\.md$/, "").split("/")
+
+ current = structure
+ path_parts[0..-2].each do |part|
+ current[part] ||= {}
+ current = current[part]
+ end
+ current[path_parts.last] = relative_path.sub(/\.md$/, "")
+ end
+
+ structure
+ end
+
+ def build_breadcrumbs(path)
+ parts = path.split("/")
+ breadcrumbs = [{ name: "Docs", path: docs_path, is_link: true }]
+
+ current_path = ""
+ parts.each_with_index do |part, index|
+ current_path = current_path.empty? ? part : "#{current_path}/#{part}"
+
+ # Check if this path exists as a file
+ file_exists = File.exist?(Rails.root.join("docs", "#{current_path}.md")) ||
+ File.exist?(Rails.root.join("docs", current_path, "index.md"))
+
+ # Only make it a link if the file exists, or if it's the current page (last item)
+ if file_exists || index == parts.length - 1
+ breadcrumbs << { name: part.titleize, path: doc_path(current_path), is_link: true }
+ else
+ breadcrumbs << { name: part.titleize, path: nil, is_link: false }
+ end
+ end
+
+ breadcrumbs
+ end
+
+ def extract_title(content)
+ lines = content.lines
+ title_line = lines.find { |line| line.start_with?("# ") }
+ title_line&.sub(/^# /, "")&.strip
+ end
+
+ def render_markdown(content)
+ renderer = Redcarpet::Render::HTML.new(
+ filter_html: true,
+ no_links: false,
+ no_images: false,
+ with_toc_data: true,
+ hard_wrap: true
+ )
+
+ markdown = Redcarpet::Markdown.new(
+ renderer,
+ autolink: true,
+ tables: true,
+ fenced_code_blocks: true,
+ strikethrough: true,
+ lax_spacing: true,
+ space_after_headers: true,
+ superscript: false
+ )
+
+ markdown.render(content)
+ end
+
+ def render_not_found
+ render file: "#{Rails.root}/public/404.html", status: :not_found, layout: false
+ end
+end
diff --git a/app/views/docs/index.html.erb b/app/views/docs/index.html.erb
new file mode 100644
index 0000000..1ac37fd
--- /dev/null
+++ b/app/views/docs/index.html.erb
@@ -0,0 +1,53 @@
+<% content_for :title, "Documentation" %>
+<% content_for :meta_description, "Complete documentation for Hackatime - learn how to track your coding time and use our API." %>
+
+
+
+ Need help getting started?
+
+
+ Learn How to Use Hackatime
+
+
+ Documentation for tracking your coding time and building with our API
+
+
+
+ Hackatime helps you automatically track the time you spend coding on different projects.
+ Whether you're just getting started or building integrations with our API,
+ you'll find everything you need here.
+
+
+
Getting Started
+
+ New to Hackatime? Here's how to get up and running:
+
+
+ - <%= link_to "Quick Start Guide", doc_path("getting-started/quick-start") %> - Get tracking in 5 minutes
+ - <%= link_to "Installation", doc_path("getting-started/installation") %> - Install plugins for your editor
+ - <%= link_to "Configuration", doc_path("getting-started/configuration") %> - Set up project mapping and preferences
+
+
+
API Reference
+
+ Build your own tools and integrations:
+
+
+ - <%= link_to "API Overview", doc_path("api/overview") %> - Introduction to our API
+ - <%= link_to "Authentication", doc_path("api/authentication") %> - How to authenticate requests
+ - <%= link_to "Endpoints", doc_path("api/endpoints") %> - Complete endpoint reference
+
+
+
+ Need help? Jump into Hack Club Slack
+ or create an issue on GitHub.
+ The community is always happy to help!
+
+
+
+ Found an issue with this page?
+
+ Edit it on GitHub
+ - we'd love your help making the docs better!
+
+
diff --git a/app/views/docs/show.html.erb b/app/views/docs/show.html.erb
new file mode 100644
index 0000000..54f9044
--- /dev/null
+++ b/app/views/docs/show.html.erb
@@ -0,0 +1,30 @@
+<% content_for :title, @title %>
+<% content_for :meta_description, "#{@title} - Hackatime Documentation" %>
+
+
+
+ <% @breadcrumbs.each_with_index do |crumb, index| %>
+ <% if index == @breadcrumbs.length - 1 %>
+ <%= crumb[:name] %>
+ <% else %>
+ <% if crumb[:is_link] && crumb[:path] %>
+ <%= link_to crumb[:name], crumb[:path] %>
+ <% else %>
+ <%= crumb[:name] %>
+ <% end %>
+ /
+ <% end %>
+ <% end %>
+
+
+
+ <%= raw @rendered_content %>
+
+
+
+ Found an issue with this page?
+
+ Edit it on GitHub
+ - we'd love your help making the docs better!
+
+
diff --git a/app/views/shared/_nav.html.erb b/app/views/shared/_nav.html.erb
index 259bb31..c8637c4 100644
--- a/app/views/shared/_nav.html.erb
+++ b/app/views/shared/_nav.html.erb
@@ -40,6 +40,11 @@
Settings
<% end %>
+
+ <%= link_to docs_path, class: "nav-item #{current_page?(docs_path) || request.path.start_with?('/docs') ? 'active' : ''}" do %>
+ Docs
+ <% end %>
+
<%= link_to signout_path, class: "nav-item", data: { turbo_method: :delete } do %>
Logout
diff --git a/config/routes.rb b/config/routes.rb
index 366dc09..02d534a 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -68,6 +68,10 @@ Rails.application.routes.draw do
delete "signout", to: "sessions#destroy", as: "signout"
resources :leaderboards, only: [ :index ]
+
+ # Docs routes
+ 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
diff --git a/docs/api/authentication.md b/docs/api/authentication.md
new file mode 100644
index 0000000..af93ced
--- /dev/null
+++ b/docs/api/authentication.md
@@ -0,0 +1,102 @@
+# Authentication
+
+Learn how to authenticate with the Hackatime API.
+
+## API Key
+
+All API requests require authentication using your personal API key.
+
+### Getting Your API Key
+
+1. Log in to [Hackatime](/)
+2. Go to [Settings](/my/settings)
+3. Copy your API key from the settings page
+
+### Using Your API Key
+
+Include your API key in the Authorization header:
+
+```bash
+curl -H "Authorization: Bearer YOUR_API_KEY" \
+ https://hackatime.hackclub.com/api/v1/stats
+```
+
+## Alternative Authentication Methods
+
+### Query Parameter
+
+You can also pass your API key as a query parameter:
+
+```bash
+curl "https://hackatime.hackclub.com/api/v1/stats?api_key=YOUR_API_KEY"
+```
+
+**Note**: Using the Authorization header is recommended for security.
+
+## Testing Authentication
+
+Test your API key with a simple request:
+
+```bash
+curl -H "Authorization: Bearer YOUR_API_KEY" \
+ https://hackatime.hackclub.com/api/v1/stats
+```
+
+A successful response will look like:
+
+```json
+{
+ "data": {
+ "total_seconds": 12345,
+ "languages": [...],
+ "editors": [...]
+ },
+ "success": true
+}
+```
+
+## Error Responses
+
+### Invalid API Key
+
+```json
+{
+ "error": "Invalid API key",
+ "success": false
+}
+```
+
+### Missing API Key
+
+```json
+{
+ "error": "API key required",
+ "success": false
+}
+```
+
+## Rate Limiting
+
+* **Authenticated requests**: 100 per minute
+* **Unauthenticated requests**: 10 per minute
+
+Rate limit headers are included in responses:
+* `X-RateLimit-Limit`: Your rate limit
+* `X-RateLimit-Remaining`: Requests remaining
+* `X-RateLimit-Reset`: Unix timestamp when limit resets
+
+## Security Best Practices
+
+* **Never commit API keys** to version control
+* **Use environment variables** to store API keys
+* **Rotate keys regularly** if they may be compromised
+* **Use HTTPS** for all API requests
+
+## Need Help?
+
+If you're having authentication issues, check:
+1. Your API key is correct
+2. You're using the right API endpoint
+3. Your request headers are properly formatted
+
+Still stuck? Reach out in [Hack Club Slack](https://hackclub.slack.com)!
diff --git a/docs/api/overview.md b/docs/api/overview.md
new file mode 100644
index 0000000..aa03cf6
--- /dev/null
+++ b/docs/api/overview.md
@@ -0,0 +1,49 @@
+# API Overview
+
+The Hackatime API is compatible with WakaTime's API, allowing you to access your coding time data programmatically.
+
+## Quick Start
+
+Get your API key from your Hackatime dashboard settings, then make requests to:
+
+```
+https://hackatime.hackclub.com/api/v1/
+```
+
+## Authentication
+
+Include your API key in requests using either method:
+
+**Authorization Header** (recommended):
+```
+Authorization: Bearer YOUR_API_KEY
+```
+
+**Query Parameter**:
+```
+?api_key=YOUR_API_KEY
+```
+
+## Basic Example
+
+Get your coding stats:
+
+```bash
+curl -H "Authorization: Bearer YOUR_API_KEY" \
+ https://hackatime.hackclub.com/api/v1/users/current/stats
+```
+
+## WakaTime Compatibility
+
+Since Hackatime is compatible with WakaTime's API, you can:
+- Use existing WakaTime libraries and SDKs
+- Point WakaTime tools to `https://hackatime.hackclub.com`
+- Import/export data between services
+
+## Common Endpoints
+
+- **User Stats**: `/api/v1/users/current/stats` - Your coding statistics
+- **Heartbeats**: `/api/v1/users/current/heartbeats` - Raw activity data
+- **Leaderboard**: `/api/v1/leaders` - Community leaderboard data
+
+For detailed endpoint documentation, refer to the [WakaTime API docs](https://wakatime.com/developers) - most endpoints work identically with Hackatime.
diff --git a/docs/getting-started/configuration.md b/docs/getting-started/configuration.md
new file mode 100644
index 0000000..d0935eb
--- /dev/null
+++ b/docs/getting-started/configuration.md
@@ -0,0 +1,76 @@
+# Configuration
+
+Customize your Hackatime setup to get the most accurate time tracking.
+
+## Project Mapping
+
+Map your local project folders to GitHub repositories for better insights:
+
+1. Go to [Projects](/my/projects)
+2. Click on a project name
+3. Enter the GitHub repository URL
+4. Save your changes
+
+This helps link your coding time to specific repositories and enables features like:
+* Repository links in leaderboards
+* Better project organization
+* Integration with GitHub stats
+
+## Time Zone
+
+Set your correct time zone in [Settings](/my/settings) to ensure accurate daily/weekly statistics.
+
+## Privacy Settings
+
+### What Gets Tracked
+
+Hackatime tracks:
+* **File names and paths** (for project detection)
+* **Programming languages**
+* **Editors used**
+* **Time spent coding**
+
+### What Doesn't Get Tracked
+
+Hackatime **never** tracks:
+* File contents or code
+* Keystrokes
+* Screenshots
+* Passwords or sensitive data
+
+## WakaTime Configuration
+
+You can also configure the WakaTime plugin directly:
+
+### Exclude Files
+
+Create a `.wakatime-project` file in your project root:
+
+```
+[settings]
+debug = false
+hidefilenames = false
+exclude =
+ ^/var/
+ \.log$
+ /node_modules/
+ /vendor/
+```
+
+### Include Only Specific Files
+
+```
+[settings]
+include_only_with_project_file = true
+```
+
+## API Configuration
+
+For advanced users, you can configure the API endpoint in your WakaTime settings:
+
+* **API URL**: `https://hackatime.hackclub.com/api/hackatime/v1`
+* **Timeout**: 30 seconds (default)
+
+## Need Help?
+
+If you need assistance with configuration, reach out in [Hack Club Slack](https://hackclub.slack.com) or check our [troubleshooting guide](/docs/getting-started/troubleshooting).
diff --git a/docs/getting-started/installation.md b/docs/getting-started/installation.md
new file mode 100644
index 0000000..a5d122b
--- /dev/null
+++ b/docs/getting-started/installation.md
@@ -0,0 +1,59 @@
+# Installation
+
+Follow these steps to set up time tracking with Hackatime.
+
+## Prerequisites
+
+Before you begin, make sure you have:
+* A code editor (VS Code, IntelliJ, Vim, etc.)
+* A [Hackatime account](/)
+
+## Install the WakaTime Plugin
+
+Hackatime uses the WakaTime ecosystem for time tracking. Install the appropriate plugin for your editor:
+
+### Visual Studio Code
+
+1. Open VS Code
+2. Go to Extensions (Ctrl+Shift+X)
+3. Search for "WakaTime"
+4. Click Install
+
+### IntelliJ IDEA / PyCharm / WebStorm
+
+1. Go to File → Settings (or IntelliJ IDEA → Preferences on macOS)
+2. Select Plugins
+3. Search for "WakaTime"
+4. Install and restart your IDE
+
+### Vim/Neovim
+
+For Vim users, install vim-wakatime:
+
+```bash
+git clone https://github.com/wakatime/vim-wakatime.git ~/.vim/bundle/vim-wakatime
+```
+
+### Sublime Text
+
+1. Open Package Control (Ctrl+Shift+P)
+2. Type "Install Package"
+3. Search for "WakaTime"
+4. Install the package
+
+## Configure Your API Key
+
+1. Get your API key from your [Hackatime settings](/my/settings)
+2. When prompted by the plugin, enter:
+ * **API Key**: Your Hackatime API key
+ * **API URL**: `https://hackatime.hackclub.com/api/hackatime/v1`
+
+## Verify Installation
+
+After setup, your coding time should automatically start being tracked. Check your [dashboard](/) to see your stats!
+
+## Troubleshooting
+
+* **Not seeing data?** Make sure your API URL is correctly set
+* **Plugin not working?** Try restarting your editor
+* **Need help?** Reach out in the [Hack Club Slack](https://hackclub.slack.com)
diff --git a/docs/getting-started/quick-start.md b/docs/getting-started/quick-start.md
new file mode 100644
index 0000000..4a3a011
--- /dev/null
+++ b/docs/getting-started/quick-start.md
@@ -0,0 +1,48 @@
+# Quick Start Guide
+
+Get up and running with Hackatime in just a few minutes!
+
+## Step 1: Create Your Account
+
+Visit [hackatime.hackclub.com](https://hackatime.hackclub.com) and sign up using:
+- Your Hack Club Slack account (recommended)
+- GitHub account
+- Email address
+
+## Step 2: Get Your API Key
+
+1. After signing in, go to your dashboard
+2. Click on "Setup Tracking" or visit your settings
+3. Copy your unique API key - you'll need this for the next step
+
+## Step 3: Install WakaTime Plugin
+
+Hackatime works with all WakaTime plugins. Choose your editor:
+
+**VS Code**:
+1. Open Extensions (Ctrl/Cmd + Shift + X)
+2. Search for "WakaTime"
+3. Install and restart VS Code
+
+**Other Editors**:
+- **IntelliJ/PyCharm**: Settings → Plugins → Install "WakaTime"
+- **Vim/Neovim**: Install vim-wakatime plugin
+- **Sublime Text**: Install via Package Control
+- **Atom**: Install wakatime package
+
+## Step 4: Configure the Plugin
+
+1. The plugin will prompt you for your API key on first use
+2. Paste your Hackatime API key from Step 2
+3. The plugin will automatically configure itself for Hackatime
+
+## Step 5: Start Coding!
+
+That's it! Just start coding normally and your time will be automatically tracked. Visit your Hackatime dashboard to see your coding stats, language breakdowns, and compare with other Hack Club members on the leaderboard.
+
+## Troubleshooting
+
+If you're having issues, make sure:
+- Your API key is correctly configured
+- Your editor plugin is the latest version
+- Check the Hack Club Slack #hackatime channel for help
diff --git a/docs/index.md b/docs/index.md
new file mode 100644
index 0000000..4d0f3f8
--- /dev/null
+++ b/docs/index.md
@@ -0,0 +1,28 @@
+# Welcome to Hackatime
+
+Hackatime is Hack Club's coding time tracker that helps you understand your programming habits and productivity patterns.
+
+## What is Hackatime?
+
+Hackatime automatically tracks the time you spend coding across different projects, languages, and editors. It's built to be compatible with the WakaTime ecosystem, so you can use existing WakaTime plugins and tools while enjoying a community-focused experience designed for Hack Club members.
+
+## How it Works
+
+1. **Install a WakaTime Plugin**: Add the WakaTime extension to your favorite code editor (VS Code, IntelliJ, Vim, etc.)
+2. **Configure Your API Key**: Get your API key from Hackatime and configure it in your editor
+3. **Start Coding**: Your coding time is automatically tracked as you write code
+4. **View Your Stats**: See your coding activity, most-used languages, projects, and productivity patterns on your dashboard
+
+## Key Features
+
+- **Automatic Time Tracking**: No need to start/stop timers - just code naturally
+- **Language & Project Insights**: See which languages and projects you spend the most time on
+- **Leaderboards**: Compare your coding activity with other Hack Club members
+- **WakaTime Compatibility**: Use any existing WakaTime plugin or tool
+- **Privacy First**: Your code content is never tracked, only metadata like file types and project names
+
+## Getting Started
+
+Ready to start tracking your coding time? Check out our [Quick Start Guide](/docs/getting-started/quick-start) to get up and running in minutes.
+
+**Happy hacking!** 🚀
diff --git a/public/404.html b/public/404.html
index 33473d1..83c1f85 100644
--- a/public/404.html
+++ b/public/404.html
@@ -110,15 +110,7 @@
Hey! Congrats! You probably just found a bug!
This is the Hackatime v2 beta, after all.
Can you take a screenshot of this page and email max@hackclub.com describing what you did to get here?
- Also please make sure the current URL is visible in the screenshot.
- Max is at Scrapyard Columbus this weekend working on Hackatime v2 - help him!
-
-
- Hey! Congrats! You probably just found a bug!
- This is the Hackatime v2 beta, after all.
- Can you take a screenshot of this page and email max@hackclub.com
- describing what you did to get here? Please make sure the current URL
- is visible in the screenshot.
+ Also please make sure the current URL is visible in the screenshot.
diff --git a/public/robots.txt b/public/robots.txt
index 4a4ee81..f5fcb74 100644
--- a/public/robots.txt
+++ b/public/robots.txt
@@ -1,5 +1,18 @@
-# See https://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file
+# Hackatime robots.txt - Allow search engines to index our public documentation
+# See https://www.robotstxt.org/robotstxt.html for documentation
User-agent: *
-Disallow: /leaderboards
+# Allow all documentation to be indexed
+Allow: /docs
+
+# Block private user data from indexing
+Disallow: /leaderboards
+Disallow: /my/
+Disallow: /admin/
+Disallow: /api/
+Disallow: /auth/
+Disallow: /users/*/projects
+
+# Crawl-delay to be respectful of server resources
+Crawl-delay: 1