Compare commits

...

18 commits

Author SHA1 Message Date
0a046ed9c1 Oh right, hide NeoPass on settings page unless you set the magic param!
Simplified this a bit into a helper. It's kinda odd to me, but
convenient for this moment, that Rails allows views to read `params`! I
guess it's for escape hatches exactly like this! lol
2024-04-08 05:34:47 -07:00
5cc219c795 Connect a NeoPass to an existing account
including validation logic to make sure it's not already connected to
another one!

The `intent` param on the NeoPass form is part of the key! Thanks
OmniAuth for making it easy to pass that data through!
2024-04-08 05:33:58 -07:00
09bccd41da Oops, stop saying "Welcome back" for new NeoPass users!
Ahh I see, if you do a no-op update, it still clears the
`previously_new_record?` state, so our NeoPass controller thinks this
account already existed. Instead, let's only do this update if it's an
account that already exists, instead of depending on the no-op-iness!
2024-04-08 05:00:27 -07:00
889c454c65 Oops, fix a redirect URL I missed when ejecting from Devise controller 2024-04-08 04:32:34 -07:00
f6d3992045 Don't require current_password for settings if user doesn't have one 2024-04-08 04:13:07 -07:00
0f5bb2a861 Oops, stay signed in when changing password 2024-04-08 04:12:46 -07:00
ae2b62956a Eject AuthUsersController from the default Devise controller
I'm getting ready to add handling for "what if you don't *have* a
current password*??", so it seems like the right way to do that is to
just eject the controller and start customizing!
2024-04-08 04:02:54 -07:00
3e92d89765 Fix error when multiple accounts have a blank email address 2024-04-08 03:46:41 -07:00
ed89380152 Oops, allow NeoPass to be disconnected if you have no email address
That is, you're required to add a password *or* an email before
disconnecting your NeoPass, but idk, I think it's rude to demand an
email from someone for the sake of *disconnection*. Email is no longer
required for accounts that already exist!
2024-04-07 08:42:41 -07:00
b5e203c0e5 Oops, fix settings page styles when validation fails
Ahh right, when I fixed this for Turbo, I forgot this page can also
render in the `update` action when it fails!
2024-04-07 08:32:38 -07:00
54a052848a Disable disconnecting NeoPass if no password/email is set
Just as a precautionary thing! Seems polite.
2024-04-07 08:27:02 -07:00
b827727102 Rename AuthUser#neopass? -> AuthUser#uses_neopass?
This is more consistent with the `uses_omniauth?` we already have, and
it also will help for the next change, where I want a `uses_password?`
method (and using the name `password?` breaks some of Devise's
validation code).
2024-04-07 08:12:38 -07:00
89fc99c918 Oops, fix bug for authorizing the NeoPass disconnect endpoint
Ahh right, in development `User` and `AuthUser` will have the same ID,
but that got messed up early on for us in production DTI 😅

Here, we switch the form to reference the `User` instead of the
`AuthUser` (to get the ID right), then we also change how we compare
the IDs, because `User#to_param` appends extra text onto the ID after
the number!
2024-04-07 08:11:22 -07:00
66978bf5a0 Oops, fix Settings page styles spreading to other pages via Turbo
Oh right, I made this mistake before too, lol! Once stylesheets are
added, they don't go away!
2024-04-07 08:04:32 -07:00
88a2688ac8 Add form to disconnect NeoPass
Can't connect it back yet! But you can disconnect it! :3
2024-04-07 07:52:23 -07:00
21b967f83d Add some NeoPass info to the Settings page, if you have one
No buttons to change it or anything, or to link if you don't! Just a
basic display and explanation!
2024-04-07 07:17:33 -07:00
d5c3bc087e Track neopass_email when logging in with NeoPass
Gonna use this in the Settings UI to communicate what NeoPass you're
connected to!
2024-04-07 07:17:07 -07:00
82aea20679 Redesign user settings form
Motivation is that I wanna add NeoPass stuff to here! But also like,
it's looked bad for a long time, let's clean it up!! (I just used the
Devise default without any styling at all lol)
2024-04-07 06:43:29 -07:00
21 changed files with 459 additions and 91 deletions

View file

@ -82,7 +82,7 @@ $container_width: 800px
input, button, select, label
cursor: pointer
input[type=text], input[type=password], input[type=search], input[type=number], select, textarea
input[type=text], input[type=password], input[type=search], input[type=number], input[type=email], select, textarea
border-radius: 3px
background: #fff
border: 1px solid $input-border-color

View file

@ -0,0 +1,56 @@
@import "../partials/clean/constants"
body.auth_users-edit, body.auth_users-update
.settings-form
border: 1px solid $module-border-color
background: $module-bg-color
border-radius: 1em
padding: 1em 1.25em
&:not(:last-of-type)
margin-bottom: 2em
h2
font-size: 1.5rem
margin-bottom: .25em
.hint
font-style: italic
font-size: .85em
opacity: .9
fieldset
padding-block: .5em
fieldset:not(:last-of-type)
border-bottom: 1px solid $module-border-color
margin-bottom: .5em
.field
margin-bottom: 1em
.field_with_errors
display: inline
label
font-weight: bold
.error-explanation
color: $error-color
background: $error-bg-color
border: 1px solid $error-border-color
border-radius: .5em
padding: .5em
margin-bottom: .5em
header
font-weight: bold
ul
padding-left: 2em
.neopass-info
margin-bottom: .5em
.neopass-explanation
font-size: .85em

View file

@ -41,17 +41,19 @@
padding: .5em .75em .45em
color: #fff
text-decoration: none
-moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.5)
-webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.5)
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.5)
text-shadow: 0 -1px 1px rgba(0, 0, 0, 0.25)
border-bottom: 1px solid rgba(0, 0, 0, 0.25)
position: relative
font-weight: bold
line-height: 1
&:hover
&:hover:not(:disabled)
color: #fff
&:active
&:active:not(:disabled)
transform: translateY(1px)
&:disabled
background: #999999 image-url("alert-overlay.png") repeat-x
cursor: not-allowed
=reset-awesome-button
border-radius: 0

View file

@ -12,7 +12,6 @@ class ApplicationController < ActionController::Base
before_action :set_locale
before_action :configure_permitted_parameters, if: :devise_controller?
before_action :check_neopass_access, if: :devise_controller?
before_action :save_return_to_path,
if: ->(c) { c.controller_name == 'sessions' && c.action_name == 'new' }
@ -88,12 +87,6 @@ class ApplicationController < ActionController::Base
devise_parameter_sanitizer.permit(:account_update, keys: [:email])
end
def check_neopass_access
@can_use_neopass = (
params[:neopass] == Rails.configuration.neopass_access_secret
)
end
def save_return_to_path
if params[:return_to]
Rails.logger.debug "Saving return_to path: #{params[:return_to].inspect}"

View file

@ -0,0 +1,60 @@
class AuthUsersController < ApplicationController
before_action :authenticate_user!, except: [:new, :create]
def create
@auth_user = AuthUser.create(auth_user_params)
if @auth_user.persisted?
sign_in :auth_user, @auth_user
flash[:notice] = "Welcome to Dress to Impress, #{@auth_user.name}! 💖"
redirect_to root_path
else
render action: :new, status: :unprocessable_entity
end
end
def edit
@auth_user = current_auth_user
end
def new
@auth_user = AuthUser.new
end
def update
@auth_user = load_auth_user
# If the user has a password, then the `current_password` field is required
# when updating. If not, then it's not!
success = @auth_user.uses_password? ?
@auth_user.update_with_password(auth_user_params) :
@auth_user.update(auth_user_params)
if success
# NOTE: Changing the password will sign you out, so make sure we stay
# signed in!
bypass_sign_in @auth_user, scope: :auth_user
flash[:notice] = "Settings successfully saved."
redirect_to action: :edit
else
render action: :edit, status: :unprocessable_entity
end
end
private
def auth_user_params
params.require(:auth_user).permit(:name, :email, :password,
:password_confirmation, :current_password)
end
def load_auth_user
# Well, what we *actually* do is just use `current_auth_user`, and enforce
# that the provided user ID matches. The user ID param is only really for
# REST semantics and such!
raise AccessDenied unless auth_user_signed_in?
raise AccessDenied unless current_auth_user.id == params[:id].to_i
current_auth_user
end
end

View file

@ -1,8 +1,31 @@
class Devise::OmniauthCallbacksController < ApplicationController
rescue_from AuthUser::AuthAlreadyConnected, with: :auth_already_connected
rescue_from AuthUser::MissingAuthInfoError, with: :missing_auth_info
rescue_from ActiveRecord::RecordInvalid, with: :validation_failed
def neopass
case intent_param
when "login"
sign_in_with_neopass
when "connect"
connect_with_neopass
else
flash[:alert] = "Hrm, the NeoPass form you used was missing some " +
"\"intent\" information. That's surprising! Maybe try again?"
redirect_to root_path
end
end
def failure
flash[:warning] =
"Hrm, something went wrong in our connection to NeoPass, sorry! " +
"Maybe wait a moment and try again?"
redirect_to new_auth_user_session_path
end
private
def sign_in_with_neopass
@auth_user = AuthUser.from_omniauth(request.env["omniauth.auth"])
if @auth_user.previously_new_record?
@ -18,11 +41,20 @@ class Devise::OmniauthCallbacksController < ApplicationController
sign_in_and_redirect @auth_user, event: :authentication
end
def failure
flash[:warning] =
"Hrm, something went wrong in our connection to NeoPass, sorry! " +
"Maybe wait a moment and try again?"
redirect_to new_auth_user_session_path
def connect_with_neopass
raise AccessDenied unless auth_user_signed_in?
current_auth_user.connect_omniauth!(request.env["omniauth.auth"])
flash[:notice] = "We've successfully connected your NeoPass!"
redirect_to edit_auth_user_path
end
def auth_already_connected(error)
flash[:alert] = "This NeoPass is already connected to another account. " +
"You'll need to log out of this account, log in with that NeoPass, " +
"then disconnect it from the other account."
redirect_to return_path
end
def missing_auth_info(error)
@ -33,7 +65,7 @@ class Devise::OmniauthCallbacksController < ApplicationController
"Hrm, our connection with NeoPass didn't return the information we " +
"usually expect, sorry! If this keeps happening, please email me at " +
"matchu@openneo.net so I can help investigate!"
redirect_to new_auth_user_session_path
redirect_to return_path
end
def validation_failed(error)
@ -44,6 +76,21 @@ class Devise::OmniauthCallbacksController < ApplicationController
"Hrm, the connection with NeoPass worked, but we had trouble saving " +
"your account, sorry! If this keeps happening, please email me at " +
"matchu@openneo.net so I can help investigate!"
redirect_to new_auth_user_session_path
redirect_to return_path
end
def intent_param
request.env['omniauth.params']["intent"]
end
def return_path
case intent_param
when "login"
new_auth_user_session_path
when "connect"
edit_auth_user_path
else
root_path
end
end
end

View file

@ -0,0 +1,29 @@
class NeopassConnectionsController < ApplicationController
def destroy
@user = load_user
if @user.disconnect_neopass
flash[:notice] = "Your NeoPass has been disconnected. In the future, " +
"to log into this account, you'll need to use your password or your " +
"recovery email. You can also connect a different NeoPass, if you'd " +
"like."
else
flash[:alert] = "Whoops, there was an error disconnecting your " +
"NeoPass from your account, sorry. If this keeps happening, let us " +
"know!"
end
redirect_to edit_auth_user_path
end
private
def load_user
# Well, what we *actually* do is just use `current_user`, and enforce that
# the provided user ID matches. The user ID param is only really for REST
# semantics and such!
raise AccessDenied unless user_signed_in?
raise AccessDenied unless current_user.id == params[:user_id].to_i
current_user
end
end

View file

@ -69,6 +69,10 @@ module ApplicationHelper
end
end
def can_use_neopass
params[:neopass] == Rails.configuration.neopass_access_secret
end
def contact_email
"matchu@openneo.net"
end

View file

@ -7,8 +7,15 @@ class AuthUser < AuthRecord
validates :name, presence: true, uniqueness: {case_sensitive: false},
length: {maximum: 30}
validates :uid, uniqueness: {scope: :provider, allow_nil: true}
has_one :user, foreign_key: :remote_id, inverse_of: :auth_user
# If the email is blank, ensure that it's `nil` rather than an empty string,
# or else the database's uniqueness constraint will object to multiple users
# who all have the empty string as their email.
before_validation { self.email = nil if email.blank? }
# It's important to keep AuthUser and User in sync. When we create an AuthUser
# (e.g. through the registration process), we create a matching User, too. And
@ -33,18 +40,96 @@ class AuthUser < AuthRecord
end
def email_required?
!uses_omniauth?
# Email is required when creating a new account from scratch, but it isn't
# required when creating a new account via third-party login (e.g. it's
# already taken). It's also okay to remove your email address, though this
if new_record?
# When creating a new account, email is required when building it from
# scratch, but not required when using third-party login. This is mainly
# because third-party login can't reliably offer an unused email!
!uses_omniauth?
else
# TODO: I had wanted to make email required if you already have one, to
# make it harder to accidentally remove? I expected
# `email_before_last_save` to be the way to check this, but it
# seemed to be `nil` when calling this, go figure! For now, we're
# allowing email to be removed.
#
# NOTE: This is important for the case where you're disconnecting a
# NeoPass, but you don't have an email set, because your NeoPass
# email already belonged to another account. I don't think it makes
# sense to require people to add an alternate real email address in
# order to be able to disconnect a NeoPass from a DTI account they
# maybe even created by accident!
false
end
end
def password_required?
super && !uses_omniauth?
end
def uses_neopass?
provider == "neopass"
end
def neopass_friendly_id
neopass_email || uid
end
def uses_password?
encrypted_password?
end
def connect_omniauth!(auth)
raise MissingAuthInfoError, "Email missing" if auth.info.email.blank?
begin
update!(provider: auth.provider, uid: auth.uid,
neopass_email: auth.info.email)
rescue ActiveRecord::RecordInvalid
# If this auth is already bound to another account, raise a specific
# error about it, instead of the normal error.
if errors.where(:uid).any? { |e| e.type == :taken }
raise AuthAlreadyConnected, "there's already an account with " +
"provider #{auth.provider}, uid #{auth.uid}"
end
raise
end
end
def disconnect_neopass
# If there's no NeoPass, we're already done!
return true if !uses_neopass?
begin
# Remove all of the NeoPass fields, and return whether we were
# successful. (I don't know why it wouldn't be, but let's be resilient!)
#
# NOTE: I considered leaving `neopass_email` in place, to help us support
# users who accidentally got locked out… but I think it's more
# important to respect data privacy and not be holding onto an
# email address the user doesn't realize we have!
update(provider: nil, uid: nil, neopass_email: nil)
rescue => error
# If something strange happens, log it and gracefully return `false`!
Sentry.capture_exception error
Rails.logger.error error
false
end
end
def self.find_by_omniauth(auth)
find_by(provider: auth.provider, uid: auth.uid)
end
def self.from_omniauth(auth)
raise MissingAuthInfoError, "Email missing" if auth.info.email.blank?
transaction do
find_or_create_by!(provider: auth.provider, uid: auth.uid) do |user|
# This account is new! Let's do the initial setup.
# TODO: Can we somehow get the Neopets username if one exists, instead
# of just using total randomness?
user.name = build_unique_username
@ -55,6 +140,17 @@ class AuthUser < AuthRecord
# password recovery!)
email_exists = AuthUser.where(email: auth.info.email).exists?
user.email = auth.info.email unless email_exists
end.tap do |user|
# If this account already existed, make sure we've saved the latest
# email to `neopass_email`.
#
# We track this separately from `email`, which the user can edit, to
# use in the Settings UI to indicate what NeoPass you're linked to. (In
# practice, this *shouldn't* ever change after initial setup, because
# NeoPass emails are immutable? But why not be resilient!)
unless user.previously_new_record?
user.update!(neopass_email: auth.info.email)
end
end
end
end
@ -87,5 +183,6 @@ class AuthUser < AuthRecord
"\"#{base_name}\" are taken??)"
end
class AuthAlreadyConnected < ArgumentError;end
class MissingAuthInfoError < ArgumentError;end
end

View file

@ -4,6 +4,7 @@ class User < ApplicationRecord
PreviewTopContributorsCount = 3
belongs_to :auth_user, foreign_key: :remote_id, inverse_of: :user
delegate :disconnect_neopass, to: :auth_user
has_many :closet_hangers
has_many :closet_lists

View file

@ -0,0 +1,117 @@
<h2>Settings</h2>
<%= form_with(model: @auth_user, method: :put, class: "settings-form") do |f| %>
<h2>Your info</h2>
<%= render "devise/shared/error_messages", resource: @auth_user %>
<fieldset>
<div class="field">
<%= f.label :name, 'DTI Username' %>
<span class="hint">Use this to log in to Dress to Impress!</span>
<br />
<%= f.text_field :name, autocomplete: "username" %>
</div>
<div class="field">
<%= f.label :email %>
<span class="hint">This can help you recover your account later.</span>
<br />
<%= f.email_field :email, autocomplete: "email" %>
</div>
</fieldset>
<fieldset>
<div class="field">
<%= f.label :password, "New password" %>
<span class="hint">Leave blank if you don't want to change it.</span>
<br />
<%= f.password_field :password, autocomplete: "new-password" %>
<% if @minimum_password_length %>
<br />
<span class="hint"><%= @minimum_password_length %> characters minimum</span>
<% end %>
</div>
<div class="field">
<%= f.label :password_confirmation, "New password confirmation" %><br />
<%= f.password_field :password_confirmation, autocomplete: "new-password" %>
</div>
</fieldset>
<%# Current password is only required if you have one! %>
<% if @auth_user.uses_password? %>
<fieldset>
<div class="field">
<%= f.label :current_password %>
<span class="hint">We need your current password to confirm your changes.</span>
<br />
<%= f.password_field :current_password, autocomplete: "current-password" %>
</div>
</fieldset>
<% end %>
<div class="actions">
<%= f.submit "Save changes" %>
</div>
<% end %>
<% if @auth_user.uses_neopass? %>
<%= form_with url: user_neopass_connection_path(@auth_user.user),
method: :delete, class: "settings-form", data: {
turbo_confirm: "Are you sure? Without a NeoPass, you'll need to use " +
"your password or your recovery email \"#{@auth_user.email}\" to " +
"log in again.\n\nMake sure you have everything all set up first! " +
"Otherwise, you might be locked out of this account forever!"
} do |form|
%>
<h2>Your NeoPass</h2>
<section class="neopass-info">
<strong>
NeoPass ID:
</strong>
<%= @auth_user.neopass_friendly_id %>
</section>
<section class="neopass-explanation">
<p>
You can log into your Dress to Impress account with NeoPass, or with
your username and password. If you ever lose access to your NeoPass,
you can still use "Forgot your password?" to recover your Dress to
Impress account, using the Email saved in "Your info".
</p>
<% if !@auth_user.uses_password? && !@auth_user.email %>
<p>
You can't remove this NeoPass yet, because you need to either set a
password or a recovery email first. (Ideally both!)
</p>
<% elsif !@auth_user.uses_password? %>
<p>
Be extra careful here! Your account doesn't have a password set.
</p>
<% elsif !@auth_user.email? %>
<p>
Be extra careful here! Your account doesn't have an email set.
</p>
<% end %>
</section>
<%= form.submit "Disconnect your NeoPass",
disabled: !@auth_user.uses_password? && !@auth_user.email? %>
<% end %>
<% elsif can_use_neopass %>
<%= form_with url: auth_user_neopass_omniauth_authorize_path(intent: "connect"),
method: :post, class: "settings-form", data: {turbo: false} do |form|
%>
<h2>Your NeoPass</h2>
<section class="neopass-explanation">
<p>
If you connect a NeoPass, you can use it to log into this DTI account!
You'll still be able to use your password to log in too, and you can
disconnect this later if you'd like.
</p>
</section>
<%= form.submit "Connect your NeoPass" %>
<% end %>
<% end %>
<% content_for :stylesheets do %>
<%= stylesheet_link_tag "auth_users/edit" %>
<% end %>

View file

@ -1,7 +1,7 @@
<h2>Sign up</h2>
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<%= render "devise/shared/error_messages", resource: resource %>
<%= form_with(model: @auth_user, method: :post) do |f| %>
<%= render "devise/shared/error_messages", resource: @auth_user %>
<p>
Choose a username, and an email address we can use to reset your password.

View file

@ -1,44 +0,0 @@
<h2>Settings</h2>
<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %>
<%= render "devise/shared/error_messages", resource: resource %>
<div class="field">
<%= f.label :name, 'Username' %><br />
<%= f.text_field :name, autofocus: true, autocomplete: "username" %>
</div>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true, autocomplete: "email" %>
</div>
<% if devise_mapping.confirmable? && resource.pending_reconfirmation? %>
<div>Currently waiting confirmation for: <%= resource.unconfirmed_email %></div>
<% end %>
<div class="field">
<%= f.label :password %> <i>(leave blank if you don't want to change it)</i><br />
<%= f.password_field :password, autocomplete: "new-password" %>
<% if @minimum_password_length %>
<br />
<em><%= @minimum_password_length %> characters minimum</em>
<% end %>
</div>
<div class="field">
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation, autocomplete: "new-password" %>
</div>
<div class="field">
<%= f.label :current_password %> <i>(we need your current password to confirm your changes)</i><br />
<%= f.password_field :current_password, autocomplete: "current-password" %>
</div>
<div class="actions">
<%= f.submit "Update" %>
</div>
<% end %>
<%= link_to "Back", :back %>

View file

@ -1,10 +1,10 @@
<h2>Log in</h2>
<% if @can_use_neopass %>
<% if can_use_neopass %>
🌟✨🌟✨🌟✨🌟✨🌟
<br />
<%= button_to "Log in with NeoPass",
auth_user_neopass_omniauth_authorize_path,
auth_user_neopass_omniauth_authorize_path(intent: "login"),
data: {turbo: false} # Turbo can't handle this redirect!
%>
🌟✨🌟✨🌟✨🌟✨🌟

View file

@ -1,11 +1,9 @@
<% if resource.errors.any? %>
<div id="error_explanation" data-turbo-cache="false">
<h2>
<%= I18n.t("errors.messages.not_saved",
count: resource.errors.count,
resource: resource.class.model_name.human.downcase)
%>
</h2>
<div class="error-explanation" data-turbo-cache="false">
<header>
<%= I18n.t("errors.messages.not_saved", count: resource.errors.count,
resource: "user") %>
</header>
<ul>
<% resource.errors.full_messages.each do |message| %>
<li><%= message %></li>

View file

@ -1,19 +1,11 @@
<%- if controller_name != 'sessions' %>
<%= link_to "Log in", new_session_path(resource_name) %><br />
<%= link_to "Log in", new_auth_user_session_path %><br />
<% end %>
<%- if devise_mapping.registerable? && controller_name != 'registrations' %>
<%= link_to "Sign up", new_registration_path(resource_name) %><br />
<%- if controller_name != 'auth_users' %>
<%= link_to "Sign up", new_auth_user_path %><br />
<% end %>
<%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %>
<%= link_to "Forgot your password?", new_password_path(resource_name) %><br />
<% end %>
<%- if devise_mapping.confirmable? && controller_name != 'confirmations' %>
<%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) %><br />
<% end %>
<%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %>
<%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) %><br />
<%- if controller_name != 'passwords' && controller_name != 'registrations' %>
<%= link_to "Forgot your password?", new_auth_user_password_path %><br />
<% end %>

View file

@ -49,7 +49,7 @@
= userbar_contributions_summary(current_user)
= link_to t('.userbar.items'), user_closet_hangers_path(current_user), :id => 'userbar-items-link'
= link_to t('.userbar.outfits'), current_user_outfits_path
= link_to t('.userbar.settings'), edit_auth_user_registration_path
= link_to t('.userbar.settings'), edit_auth_user_path
= button_to t('.userbar.logout'), destroy_auth_user_session_path, method: :delete,
params: {return_to: request.fullpath}
- else

View file

@ -2,7 +2,9 @@ OpenneoImpressItems::Application.routes.draw do
root :to => 'outfits#new'
# Login and account management!
devise_for :auth_users, path: "users"
devise_for :auth_users, path: "users", skip: [:registrations]
resources :auth_users, only: [:new, :create, :update]
get '/users/edit', to: 'auth_users#edit', as: 'edit_auth_user'
# The outfit editor!
# TODO: It's a bit silly that outfits/new points to outfits#edit.
@ -66,6 +68,8 @@ OpenneoImpressItems::Application.routes.draw do
resources :neopets_connections, path: 'neopets-connections',
only: [:create, :destroy]
resource :neopass_connection, path: "neopass-connection", only: [:destroy]
end
get 'users/current-user/closet' => 'closet_hangers#index', :as => :your_items

View file

@ -0,0 +1,5 @@
class AddNeoPassEmailToUsers < ActiveRecord::Migration[7.1]
def change
add_column :users, :neopass_email, :string
end
end

View file

@ -0,0 +1,5 @@
class AddUniqueIndexForOmniauthToUsers < ActiveRecord::Migration[7.1]
def change
add_index :users, [:provider, :uid], unique: true
end
end

View file

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.1].define(version: 2024_04_01_124406) do
ActiveRecord::Schema[7.1].define(version: 2024_04_08_120359) do
create_table "users", id: { type: :integer, unsigned: true }, charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
t.string "name", limit: 30, null: false
t.string "encrypted_password", limit: 64
@ -31,7 +31,9 @@ ActiveRecord::Schema[7.1].define(version: 2024_04_01_124406) do
t.datetime "remember_created_at"
t.string "provider"
t.string "uid"
t.string "neopass_email"
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["provider", "uid"], name: "index_users_on_provider_and_uid", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
t.index ["unlock_token"], name: "index_users_on_unlock_token", unique: true
end