Finish creation and signin flow

This commit is contained in:
Kyle Drake 2013-05-25 17:09:48 -07:00
parent 4ddd244588
commit 0aaf894fa3
13 changed files with 182 additions and 43 deletions

36
app.rb
View file

@ -1,10 +1,17 @@
require './environment.rb'
use Rack::Session::Cookie, key: 'neocities',
path: '/',
expire_after: 31556926, # one year in seconds
secret: $config['session_secret']
get '/' do
dashboard_if_signed_in
slim :index
end
get '/new' do
dashboard_if_signed_in
@site = Site.new
slim :'new'
end
@ -14,23 +21,17 @@ get '/dashboard' do
end
get '/signin' do
dashboard_if_signed_in
slim :'signin'
end
post '/create' do
@site = Site.new username: params[:username], password: params[:password], email: params[:email]
dashboard_if_signed_in
@site = Site.new username: params[:username], password: params[:password], email: params[:email], new_tags: params[:tags]
if @site.valid?
DB.transaction { @site.save }
@server = Server.with_slots_available
if @server.nil?
raise 'no slots available'
end
@site.server = @server
@site.save
session[:username] = @site.username
session[:id] = @site.id
redirect '/dashboard'
else
slim :'/new'
@ -38,8 +39,10 @@ post '/create' do
end
post '/signin' do
dashboard_if_signed_in
if Site.valid_login? params[:username], params[:password]
session[:username] = params[:username]
site = Site[username: params[:username]]
session[:id] = site.id
redirect '/dashboard'
else
flash[:error] = 'Invalid login.'
@ -49,8 +52,7 @@ end
get '/signout' do
require_login
session[:username] = nil
session[:timezone] = nil
session[:id] = nil
redirect '/'
end
@ -63,5 +65,9 @@ def require_login
end
def signed_in?
!session[:username].nil?
!session[:id].nil?
end
def current_site
@site ||= Site[id: session[:id]]
end

View file

@ -1,2 +1,3 @@
development:
database: 'postgres://localhost/neocities'
database: 'postgres://localhost/neocities'
session_secret: SETSOMETHINGHERE

View file

@ -1,7 +1,8 @@
Sequel.migration do
up {
DB.create_table! :sites do
String :username, primary_key: true
primary_key :id
String :username
String :email
String :password
Integer :server_id

View file

@ -3,7 +3,6 @@ Sequel.migration do
DB.create_table! :tags do
primary_key :id
String :name
DateTime :created_at
end
}

View file

@ -1,12 +0,0 @@
Sequel.migration do
up {
DB.create_table! :site_tags do
Integer :site_id
Integer :tag_id
end
}
down {
DB.drop_table :site_tags
}
end

View file

@ -0,0 +1,12 @@
Sequel.migration do
up {
DB.create_table! :sites_tags do
foreign_key :site_id, :sites
foreign_key :tag_id, :tags
end
}
down {
DB.drop_table :sites_tags
}
end

View file

@ -1,8 +1,7 @@
class Site < Sequel::Model
MINIMUM_PASSWORD_LENGTH = 5
unrestrict_primary_key
many_to_one :server
many_to_many :tags
class << self
def valid_login?(username, plaintext)
@ -30,10 +29,37 @@ class Site < Sequel::Model
values[:password] = BCrypt::Password.create plaintext, cost: (self.class.bcrypt_cost || BCrypt::Engine::DEFAULT_COST)
end
def after_save
if @new_tag_strings
@new_tag_strings.each do |new_tag_string|
add_tag Tag[name: new_tag_string] || Tag.create(name: new_tag_string)
end
end
end
def after_create
DB['update servers set slots_available=slots_available-1 where id=?', self.server.id].first
end
def new_tags=(tags_string)
tags_string.gsub! /[^a-zA-Z0-9, ]/, ''
tags = tags_string.split ','
tags.collect! {|c| (c.match(/^\w+\s\w+/) || c.match(/^\w+/)).to_s }
@new_tag_strings = tags
end
def before_validation
self.server ||= Server.with_slots_available
end
def validate
super
if values[:username].nil? || values[:username].empty?
if server.nil?
errors.add :over_capacity, 'We are currently at capacity, and cannot create your home page. We will fix this shortly. Please come back later and try again, our apologies.'
end
if values[:username].nil? || values[:username].empty? || values[:username].match(/[^\w.-]/i)
errors.add :username, 'A valid username is required.'
end

3
models/tag.rb Normal file
View file

@ -0,0 +1,3 @@
class Tag < Sequel::Model
many_to_many :sites
end

View file

@ -0,0 +1,95 @@
require_relative './environment'
include Rack::Test::Methods
def app; App end
def status; last_response.status end
def headers; last_response.headers end
def body; last_response.body end
SimpleCov.command_name 'minitest'
describe 'index' do
it 'loads' do
get '/'
status.must_equal 200
end
end
describe 'signin' do
it 'fails for missing login' do
post '/signin', username: 'derpie', password: 'lol'
fail_signin
end
it 'fails for bad password' do
@site = Fabricate :site
post '/signin', username: @site.username, password: 'derp'
fail_signin
end
it 'fails for no input' do
post '/signin'
fail_signin
end
it 'succeeds for valid input' do
password = '1tw0rkz'
@account = Fabricate :account, password: password
post '/accounts/signin', username: @account.email, password: password
headers['Location'].must_equal 'http://example.org/dashboard'
mock_dashboard_calls @account.email
get '/dashboard'
body.must_match /Dashboard/
end
end
describe 'account creation' do
it 'fails for no input' do
post '/accounts/create'
status.must_equal 200
body.must_match /There were some errors.+Valid email address is required.+Password must be/
end
it 'fails with invalid email' do
post '/accounts/create', email: 'derplol'
status.must_equal 200
body.must_match /errors.+valid email/i
end
it 'fails with invalid password' do
post '/accounts/create', 'email@example.com', password: 'sdd'
status.must_equal 200
body.must_match /errors.+Password must be at least #{Account::MINIMUM_PASSWORD_LENGTH} characters/i
end
it 'succeeds with valid info' do
account_attributes = Fabricate.attributes_for :account
mock_dashboard_calls account_attributes[:email]
post '/accounts/create', account_attributes
status.must_equal 302
headers['Location'].must_equal 'http://example.org/dashboard'
get '/dashboard'
body.must_match /Dashboard/
end
end
describe 'temporary account login' do
end
def fail_signin
headers['Location'].must_equal 'http://example.org/'
get '/'
body.must_match /invalid signin/i
end
def api_url
uri = Addressable::URI.parse $config['bitcoind_rpchost'] ? $config['bitcoind_rpchost'] : 'http://localhost'
uri.port = 8332 if uri.port.nil?
uri.user = $config['bitcoind_rpcuser'] if uri.user.nil?
uri.password = $config['bitcoind_rpcpassword'] if uri.password.nil?
"#{uri.to_s}/"
end

View file

@ -0,0 +1,4 @@
Fabricator(:site) do
username { Faker::Internet.email }
password { 'abcde' }
end

View file

@ -1,4 +1,4 @@
.row
.span12
h1 Dashboard!
h1 Dashboard

View file

@ -19,7 +19,7 @@ html
a.brand href="/" NeoCities
ul.nav.pull-right
- if signed_in?
li.navbar-text: strong style="color: #7AB800" #{session[:username]}
li.navbar-text: strong style="color: #7AB800" #{current_site.username}
li: a href="/signout" style="color: #B94A48" Signout
- else
li: a href="/signin" <b>Sign in</b>

View file

@ -3,7 +3,7 @@
.row
.span8.offset2
.alert.alert-block.alert-error
p Please correct the following errors:
p There were errors creating your home page:
- @site.errors.each do |error|
p = error.last.first
@ -14,17 +14,17 @@
.row
.span6
p First, enter a username. This will be used to login to your site.<br>It will also be the path of your web site.
p First, enter a username. This will also be used as your site path.<br><b>Do not forget this, it will be used to sign in to and manage your home page.</b><br>It cannot contain spaces, and can only use the following characters: a-z A-Z 0-9 . _ -
.row
.span1
h5 Username
.span6
p http://neocities.org/<input name="username" type="text" placeholder="yourusername">
p http://neocities.org/<input name="username" type="text" placeholder="yourusername" value="#{@site.username}">
.row
.span6
p Next, enter a password. This will be used to allow you to login. Minimum 5 characters.
p Next, enter a password. This will be used to allow you to login. Minimum 5 characters. If you don't make it a good password, Dade Murphy from the movie Hackers will come in and steal your "garbage files".
.row
.span1
@ -34,26 +34,30 @@
.row
.span6
p Now you can enter an e-mail address. You don't have to provide one, but <b>we will not be able to reset your password without it, so don't lose your username and password if you leave this blank!</b>
p Now you can enter an e-mail address. Your e-mail address is private and we will not show it to anyone for any reason. You don't have to provide one, but <b>we will not be able to reset your password without it, so don't lose your username and password if you leave this blank!</b>
.row
.span1
h5 Email
.span6
input name="email" type="text" placeholder="youremail@example.com"
input name="email" type="text" placeholder="youremail@example.com" value="#{@site.email}"
.row
.span6
p
| Enter some tags! Tags will allow others to find your site based on your interests, or your site's theme. <b>Separate multiple tags with commas</b>. Don't think too hard about this, you can change them later. You can have a maximum of ten tags, and there is a two word per tag maximum.
| You can optionally enter some tags! Tags will allow others to find your site based on your interests, or your site's theme. <b>Separate multiple tags with commas</b>. Don't think too hard about this, you can change them later. You can have a maximum of ten tags, and there is a two word per tag maximum (extra words in a tag will be removed).
.row
.span1
h5 Tags
.span6
p: input name="tags" type="text" style="width: 400px" placeholder="pokemon, video games, bulbasaur"
p: input name="tags" type="text" style="width: 400px" placeholder="pokemon, video games, bulbasaur" value="#{params[:tags]}"
.row
.span6
h3 You're done. Just click the button below!
.row style="margin-top: 30px"
.span3.offset1
input.btn.btn-success.btn-large type="submit" value="Create Home Page"