diff --git a/app/controllers/achievements_controller.rb b/app/controllers/achievements_controller.rb
index f5ba036..919e562 100644
--- a/app/controllers/achievements_controller.rb
+++ b/app/controllers/achievements_controller.rb
@@ -2,11 +2,12 @@ class AchievementsController < ApplicationController
before_action :authenticate_user!, except: [:index, :show]
def index
- @achievements = Achievement.order(date: :desc)
+ # Scope achievements to the current user
+ @achievements = current_user.achievements.order(date: :desc)
end
def show
- @achievement = Achievement.find(params[:id])
+ @achievement = current_user.achievements.find(params[:id])
AchievementView.create(
achievement: @achievement,
user: @achievement.user,
@@ -61,4 +62,4 @@ def destroy
def achievement_params
params.require(:achievement).permit(:title, :date, :description, :icon, :url)
end
-end
\ No newline at end of file
+end
diff --git a/app/controllers/links_controller.rb b/app/controllers/links_controller.rb
index 3cb26ae..4b2baf7 100644
--- a/app/controllers/links_controller.rb
+++ b/app/controllers/links_controller.rb
@@ -1,13 +1,14 @@
class LinksController < ApplicationController
before_action :authenticate_user!, except: [:index, :show, :user_links, :track_click]
- before_action :set_theme, only: [:user_links] # Add this line
+ before_action :set_theme, only: [:user_links]
def index
- @links = Link.order(:position)
+ # Scope links to the current user
+ @links = current_user.links.order(:position)
end
def show
- @link = Link.find(params[:id])
+ @link = current_user.links.find(params[:id])
end
def new
@@ -45,17 +46,17 @@ def destroy
def user_links
@user = User.find_by(username: params[:username])
return redirect_to root_path, alert: "User not found" if @user.nil?
-
- @links = @user.links.where(hidden: false, visible: true)
- @hidden_links = @user.links.where(hidden: true)
- @pinned_links = @user.links.where(pinned: true)
+
+ @links = @user.links.visible
+ @hidden_links = @user.links.hidden
+ @pinned_links = @user.links.pinned
@achievements = @user.achievements
@user.tags = JSON.parse(@user.tags) if @user.tags.is_a?(String)
-
+
# Add debugging
Rails.logger.debug "Theme: #{@theme.inspect}"
Rails.logger.debug "Hidden Links: #{@hidden_links.inspect}"
-
+
# Render the appropriate template based on the theme
case @theme
when 'retro'
@@ -90,7 +91,6 @@ def link_params
end
def set_theme
- # Determine the theme either from a query parameter or route segment
@theme = params[:theme] || 'default'
end
-end
\ No newline at end of file
+end
diff --git a/app/controllers/users/registrations_controller.rb b/app/controllers/users/registrations_controller.rb
index 0ffead2..5221a2e 100644
--- a/app/controllers/users/registrations_controller.rb
+++ b/app/controllers/users/registrations_controller.rb
@@ -1,10 +1,10 @@
class Users::RegistrationsController < Devise::RegistrationsController
before_action :configure_permitted_parameters, if: :devise_controller?
- before_action :check_signups_enabled, only: [:new, :create]
+ before_action :check_signups_enabled, only: [:create]
def create
- # Check if sign-ups are disabled
- if !Rails.application.config.sign_ups_open
+ # Check if sign-ups are disabled and no valid invite code is provided
+ unless Rails.application.config.sign_ups_open || valid_invite_code?(params[:user][:invite_code])
redirect_to root_path, alert: "Sign-ups are currently disabled."
return
end
@@ -60,15 +60,32 @@ def update
end
end
+ private
+
+ def check_signups_enabled
+ # Check if sign-ups are disabled and no valid invite code is provided
+ if !Rails.application.config.sign_ups_open && (params[:user].blank? || !valid_invite_code?(params[:user][:invite_code]))
+ redirect_to root_path, alert: "Sign-ups are currently disabled."
+ end
+ end
+
+ def valid_invite_code?(invite_code)
+ # List of valid invite codes
+ valid_codes = ["POWEROVERWHELMING", "SWORDFISH", "HUNTER2"]
+
+ # Check if the provided invite code matches any of the valid codes, case-insensitive
+ valid_codes.any? { |code| code.casecmp(invite_code).zero? }
+ end
+
protected
def configure_permitted_parameters
- devise_parameter_sanitizer.permit(:sign_up, keys: [:email, :password, :password_confirmation, :username, :full_name, :tags, :avatar, :banner, :description, :banner_enabled, :avatar_border])
+ devise_parameter_sanitizer.permit(:sign_up, keys: [:email, :password, :password_confirmation, :username, :full_name, :tags, :avatar, :banner, :description, :banner_enabled, :avatar_border, :invite_code])
devise_parameter_sanitizer.permit(:account_update, keys: [:email, :password, :password_confirmation, :username, :full_name, :tags, :avatar, :banner, :description, :banner_enabled, :avatar_border])
end
def sign_up_params
- params.require(:user).permit(:email, :password, :password_confirmation, :username, :full_name, :tags, :avatar, :banner, :description, :banner_enabled, :avatar_border).tap do |user_params|
+ params.require(:user).permit(:email, :password, :password_confirmation, :username, :full_name, :tags, :avatar, :banner, :description, :banner_enabled, :avatar_border, :invite_code).tap do |user_params|
user_params[:tags] = user_params[:tags].split(',').map(&:strip).to_json if user_params[:tags].present?
end
end
@@ -78,12 +95,4 @@ def account_update_params
user_params[:tags] = user_params[:tags].split(',').map(&:strip).to_json if user_params[:tags].present?
end
end
-
- private
-
- def check_signups_enabled
- unless Rails.application.config.sign_ups_open
- redirect_to root_path, alert: "Sign-ups are currently disabled."
- end
- end
end
diff --git a/app/models/user.rb b/app/models/user.rb
index 74b70ce..a98a9ee 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -1,5 +1,6 @@
# app/models/user.rb
class User < ApplicationRecord
+ attr_accessor :invite_code
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
diff --git a/app/services/open_graph_image_generator.rb b/app/services/open_graph_image_generator.rb
index 2f65da5..29aae56 100644
--- a/app/services/open_graph_image_generator.rb
+++ b/app/services/open_graph_image_generator.rb
@@ -54,10 +54,17 @@ def generate
def download_image(url)
tempfile = Tempfile.new(['avatar', '.jpg'])
tempfile.binmode
- URI.open(url) do |image|
- tempfile.write(image.read)
+ begin
+ URI.open(url) do |image|
+ tempfile.write(image.read)
+ end
+ rescue OpenURI::HTTPError, Errno::ENOENT, SocketError => e
+ Rails.logger.error("Failed to download image from URL: #{url}. Error: #{e.message}. Using default image.")
+ # Use a default image if the download fails
+ default_image_path = Rails.root.join('app', 'assets', 'images', 'greg.jpg')
+ tempfile.write(File.read(default_image_path))
end
tempfile.rewind
MiniMagick::Image.open(tempfile.path)
end
-end
\ No newline at end of file
+end
diff --git a/app/views/devise/registrations/new.html.erb b/app/views/devise/registrations/new.html.erb
index cc71baa..a477fdc 100644
--- a/app/views/devise/registrations/new.html.erb
+++ b/app/views/devise/registrations/new.html.erb
@@ -1,69 +1,80 @@
Sign up
- <% if Rails.application.config.sign_ups_open %>
- <%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { class: "space-y-6" }) do |f| %>
- <%= render "devise/shared/error_messages", resource: resource %>
+ <%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { class: "space-y-6" }) do |f| %>
+ <%= render "devise/shared/error_messages", resource: resource %>
-
- <%= f.label :email, class: 'block text-lime-200 font-semibold mb-2' %>
- <%= f.email_field :email, autofocus: true, autocomplete: "email", class: 'block w-full px-4 py-2 border border-gray-700 rounded bg-gray-900 text-white focus:outline-none focus:border-lime-500' %>
-
+
+ <%= f.label :email, class: 'block text-lime-200 font-semibold mb-2' %>
+ <%= f.email_field :email, autofocus: true, autocomplete: "email", class: 'block w-full px-4 py-2 border border-gray-700 rounded bg-gray-900 text-white focus:outline-none focus:border-lime-500' %>
+
+
+
+ <%= f.label :password, class: 'block text-lime-200 font-semibold mb-2' %>
+ <% if @minimum_password_length %>
+ (<%= @minimum_password_length %> characters minimum)
+ <% end %>
+ <%= f.password_field :password, autocomplete: "new-password", class: 'block w-full px-4 py-2 border border-gray-700 rounded bg-gray-900 text-white focus:outline-none focus:border-lime-500' %>
+
-
- <%= f.label :password, class: 'block text-lime-200 font-semibold mb-2' %>
- <% if @minimum_password_length %>
- (<%= @minimum_password_length %> characters minimum)
- <% end %>
- <%= f.password_field :password, autocomplete: "new-password", class: 'block w-full px-4 py-2 border border-gray-700 rounded bg-gray-900 text-white focus:outline-none focus:border-lime-500' %>
-
+
+ <%= f.label :password_confirmation, class: 'block text-lime-200 font-semibold mb-2' %>
+ <%= f.password_field :password_confirmation, autocomplete: "new-password", class: 'block w-full px-4 py-2 border border-gray-700 rounded bg-gray-900 text-white focus:outline-none focus:border-lime-500' %>
+
-
- <%= f.label :password_confirmation, class: 'block text-lime-200 font-semibold mb-2' %>
- <%= f.password_field :password_confirmation, autocomplete: "new-password", class: 'block w-full px-4 py-2 border border-gray-700 rounded bg-gray-900 text-white focus:outline-none focus:border-lime-500' %>
-
+
+ <%= f.label :username, class: 'block text-lime-200 font-semibold mb-2' %>
+ <%= f.text_field :username, class: 'block w-full px-4 py-2 border border-gray-700 rounded bg-gray-900 text-white focus:outline-none focus:border-lime-500' %>
+
+
+
+ <%= f.label :full_name, class: 'block text-lime-200 font-semibold mb-2' %>
+ <%= f.text_field :full_name, class: 'block w-full px-4 py-2 border border-gray-700 rounded bg-gray-900 text-white focus:outline-none focus:border-lime-500' %>
+
-
- <%= f.label :username, class: 'block text-lime-200 font-semibold mb-2' %>
- <%= f.text_field :username, class: 'block w-full px-4 py-2 border border-gray-700 rounded bg-gray-900 text-white focus:outline-none focus:border-lime-500' %>
-
+
+ <%= f.label :tags, class: 'block text-lime-200 font-semibold mb-2' %>
+ <%= f.text_field :tags, value: @user.parsed_tags.join(', '), class: 'block w-full px-4 py-2 border border-gray-700 rounded bg-gray-900 text-white focus:outline-none focus:border-lime-500' %>
+
-
- <%= f.label :full_name, class: 'block text-lime-200 font-semibold mb-2' %>
- <%= f.text_field :full_name, class: 'block w-full px-4 py-2 border border-gray-700 rounded bg-gray-900 text-white focus:outline-none focus:border-lime-500' %>
-
+
+ <%= f.label :avatar, class: 'block text-lime-200 font-semibold mb-2' %>
+ <%= f.text_field :avatar, class: 'block w-full px-4 py-2 border border-gray-700 rounded bg-gray-900 text-white focus:outline-none focus:border-lime-500' %>
+
-
- <%= f.label :tags, class: 'block text-lime-200 font-semibold mb-2' %>
- <%= f.text_field :tags, value: @user.parsed_tags.join(', '), class: 'block w-full px-4 py-2 border border-gray-700 rounded bg-gray-900 text-white focus:outline-none focus:border-lime-500' %>
-
+
+ <%= f.label :avatar_border, class: 'block text-lime-200 font-semibold mb-2' %>
+ <%= f.select :avatar_border, options_for_select([
+ ['White', 'white'],
+ ['Black', 'black'],
+ ['None', 'none'],
+ ['Rainbow', 'rainbow'],
+ ['Rainbow Overlay', 'rainbow-overlay']
+ ], @user.avatar_border), {}, class: 'block w-full px-4 py-2 border border-gray-700 rounded bg-gray-900 text-white focus:outline-none focus:border-lime-500' %>
+
-
- <%= f.label :avatar, class: 'block text-lime-200 font-semibold mb-2' %>
- <%= f.text_field :avatar, class: 'block w-full px-4 py-2 border border-gray-700 rounded bg-gray-900 text-white focus:outline-none focus:border-lime-500' %>
-
+
+ <%= f.label :banner, class: 'block text-lime-200 font-semibold mb-2' %>
+ <%= f.text_field :banner, class: 'block w-full px-4 py-2 border border-gray-700 rounded bg-gray-900 text-white focus:outline-none focus:border-lime-500' %>
+
-
- <%= f.label :banner, class: 'block text-lime-200 font-semibold mb-2' %>
- <%= f.text_field :banner, class: 'block w-full px-4 py-2 border border-gray-700 rounded bg-gray-900 text-white focus:outline-none focus:border-lime-500' %>
-
+
+ <%= f.label :description, class: 'block text-lime-200 font-semibold mb-2' %>
+ <%= f.text_area :description, rows: 3, class: 'block w-full px-4 py-2 border border-gray-700 rounded bg-gray-900 text-white focus:outline-none focus:border-lime-500' %>
+
-
- <%= f.label :description, class: 'block text-lime-200 font-semibold mb-2' %>
- <%= f.text_area :description, rows: 3, class: 'block w-full px-4 py-2 border border-gray-700 rounded bg-gray-900 text-white focus:outline-none focus:border-lime-500' %>
-
+
+
+ <%= f.label :invite_code, "Invite Code", class: 'block text-lime-200 font-semibold mb-2' %>
+ <%= f.text_field :invite_code, class: 'block w-full px-4 py-2 border border-gray-700 rounded bg-gray-900 text-white focus:outline-none focus:border-lime-500' %>
+
-
- <%= f.submit "Sign up", class: 'bg-lime-500 hover:bg-lime-600 text-white font-bold py-2 px-4 rounded transition duration-300 ease-in-out focus:outline-none' %>
-
- <% end %>
- <% else %>
-
-
Sign-ups are currently disabled.
+
+ <%= f.submit "Sign up", class: 'bg-lime-500 hover:bg-lime-600 text-white font-bold py-2 px-4 rounded transition duration-300 ease-in-out focus:outline-none' %>
<% end %>
+
-
- <%= render "devise/shared/links" %>
-
+
+ <%= render "devise/shared/links" %>
diff --git a/app/views/devise/shared/_links.html.erb b/app/views/devise/shared/_links.html.erb
index 8cef40a..a435cde 100644
--- a/app/views/devise/shared/_links.html.erb
+++ b/app/views/devise/shared/_links.html.erb
@@ -2,10 +2,8 @@
<%= link_to "Log in", new_session_path(resource_name), class: "text-lime-300 hover:text-lime-100 transition duration-300 ease-in-out" %>
<% end %>
-<% if Rails.application.config.sign_ups_open %>
- <%- if devise_mapping.registerable? && controller_name != 'registrations' %>
- <%= link_to "Sign up", new_registration_path(resource_name), class: "text-lime-300 hover:text-lime-100 transition duration-300 ease-in-out" %>
- <% end %>
+<% if devise_mapping.registerable? && controller_name != 'registrations' %>
+ <%= link_to "Sign up", new_registration_path(resource_name), class: "text-lime-300 hover:text-lime-100 transition duration-300 ease-in-out" %>
<% end %>
<%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %>
@@ -24,4 +22,4 @@
<%- resource_class.omniauth_providers.each do |provider| %>
<%= button_to "Sign in with #{OmniAuth::Utils.camelize(provider)}", omniauth_authorize_path(resource_name, provider), data: { turbo: false }, class: "mt-2 bg-gray-600 hover:bg-gray-700 text-white font-bold py-2 px-4 rounded transition duration-300 ease-in-out focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-opacity-50" %>
<% end %>
-<% end %>
\ No newline at end of file
+<% end %>
diff --git a/config/routes.rb b/config/routes.rb
index f5ccce9..cb4eb19 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -1,4 +1,6 @@
Rails.application.routes.draw do
+ # Static and more specific routes first
+
get 'analytics/index'
# Devise routes for user registration
@@ -19,10 +21,10 @@
# Root route
root to: 'pages#home'
- # Static routes should be defined before dynamic routes
+ # Routes for achievements
resources :achievements, only: [:index, :show, :new, :create, :edit, :update, :destroy]
- # Routes for links with standard RESTful actions - move these above the dynamic route
+ # Routes for links with standard RESTful actions
resources :links do
member do
get :track_click
diff --git a/spec/controllers/achievements_controller_spec.rb b/spec/controllers/achievements_controller_spec.rb
index d4fa78f..df439e2 100644
--- a/spec/controllers/achievements_controller_spec.rb
+++ b/spec/controllers/achievements_controller_spec.rb
@@ -5,6 +5,10 @@
let(:user) { create(:user) }
let(:achievement) { create(:achievement, user: user) }
+ before do
+ sign_in user # Sign in the user before each test
+ end
+
describe "GET #index" do
it "returns a success response" do
get :index
@@ -38,7 +42,6 @@
describe "GET #new" do
it "returns a success response" do
- sign_in user
get :new
expect(response).to be_successful
end
@@ -47,7 +50,6 @@
describe "POST #create" do
context "with valid params" do
it "creates a new Achievement" do
- sign_in user
expect {
post :create, params: { achievement: attributes_for(:achievement) }
}.to change(Achievement, :count).by(1)
@@ -60,7 +62,6 @@
let(:new_attributes) { { title: "New Title" } }
it "updates the requested achievement" do
- sign_in user
put :update, params: { id: achievement.to_param, achievement: new_attributes }
achievement.reload
expect(achievement.title).to eq("New Title")
@@ -70,11 +71,10 @@
describe "DELETE #destroy" do
it "destroys the requested achievement" do
- sign_in user
achievement # ensure achievement is created before the expect block
expect {
delete :destroy, params: { id: achievement.to_param }
}.to change(Achievement, :count).by(-1)
end
end
-end
\ No newline at end of file
+end
diff --git a/spec/controllers/links_controller_spec.rb b/spec/controllers/links_controller_spec.rb
index ea77088..aaeaef1 100644
--- a/spec/controllers/links_controller_spec.rb
+++ b/spec/controllers/links_controller_spec.rb
@@ -5,6 +5,10 @@
let(:user) { create(:user) }
let(:link) { create(:link, user: user) }
+ before do
+ sign_in user # Sign in the user before each test
+ end
+
describe "GET #index" do
it "returns a success response" do
get :index
@@ -21,7 +25,6 @@
describe "GET #new" do
it "returns a success response" do
- sign_in user
get :new
expect(response).to be_successful
end
@@ -30,7 +33,6 @@
describe "POST #create" do
context "with valid params" do
it "creates a new Link" do
- sign_in user
expect {
post :create, params: { link: attributes_for(:link) }
}.to change(Link, :count).by(1)
@@ -43,7 +45,6 @@
let(:new_attributes) { { title: "New Title" } }
it "updates the requested link" do
- sign_in user
put :update, params: { id: link.to_param, link: new_attributes }
link.reload
expect(link.title).to eq("New Title")
@@ -53,7 +54,6 @@
describe "DELETE #destroy" do
it "destroys the requested link" do
- sign_in user
link # ensure link is created before the expect block
expect {
delete :destroy, params: { id: link.to_param }
diff --git a/spec/controllers/users/registrations_controller_spec.rb b/spec/controllers/users/registrations_controller_spec.rb
index 371caa5..d58e0ce 100644
--- a/spec/controllers/users/registrations_controller_spec.rb
+++ b/spec/controllers/users/registrations_controller_spec.rb
@@ -8,7 +8,7 @@
describe "POST #create" do
let(:valid_attributes) {
{ email: "test@example.com", password: "password", password_confirmation: "password",
- username: "testuser", full_name: "Test User", tags: "tag1,tag2", avatar_border: "white" }
+ username: "testuser", full_name: "Test User", tags: "tag1,tag2", avatar_border: "white", invite_code: "POWEROVERWHELMING" }
}
context "when sign-ups are enabled" do
@@ -35,13 +35,24 @@
allow(Rails.application.config).to receive(:sign_ups_open).and_return(false)
end
- it "does not create a new User and redirects to root path" do
+ it "does not create a new User without a valid invite code and redirects to root path" do
+ invalid_attributes = valid_attributes.merge(invite_code: "INVALIDCODE")
expect {
- post :create, params: { user: valid_attributes }
+ post :create, params: { user: invalid_attributes }
}.not_to change(User, :count)
expect(response).to redirect_to(root_path)
expect(flash[:alert]).to eq("Sign-ups are currently disabled.")
end
+
+ it "creates a new User with a valid invite code" do
+ expect(controller).to receive(:after_sign_up_path_for).with(instance_of(User)).and_return("/path/to/redirect")
+
+ expect {
+ post :create, params: { user: valid_attributes }
+ }.to change(User, :count).by(1)
+
+ expect(response).to redirect_to("/path/to/redirect")
+ end
end
end