mirror of
https://github.com/neocities/neocities.git
synced 2025-04-28 11:12:30 +02:00
Finish creation and signin flow
This commit is contained in:
parent
4ddd244588
commit
0aaf894fa3
13 changed files with 182 additions and 43 deletions
36
app.rb
36
app.rb
|
@ -1,10 +1,17 @@
|
||||||
require './environment.rb'
|
require './environment.rb'
|
||||||
|
|
||||||
|
use Rack::Session::Cookie, key: 'neocities',
|
||||||
|
path: '/',
|
||||||
|
expire_after: 31556926, # one year in seconds
|
||||||
|
secret: $config['session_secret']
|
||||||
|
|
||||||
get '/' do
|
get '/' do
|
||||||
|
dashboard_if_signed_in
|
||||||
slim :index
|
slim :index
|
||||||
end
|
end
|
||||||
|
|
||||||
get '/new' do
|
get '/new' do
|
||||||
|
dashboard_if_signed_in
|
||||||
@site = Site.new
|
@site = Site.new
|
||||||
slim :'new'
|
slim :'new'
|
||||||
end
|
end
|
||||||
|
@ -14,23 +21,17 @@ get '/dashboard' do
|
||||||
end
|
end
|
||||||
|
|
||||||
get '/signin' do
|
get '/signin' do
|
||||||
|
dashboard_if_signed_in
|
||||||
slim :'signin'
|
slim :'signin'
|
||||||
end
|
end
|
||||||
|
|
||||||
post '/create' do
|
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?
|
if @site.valid?
|
||||||
|
DB.transaction { @site.save }
|
||||||
|
|
||||||
@server = Server.with_slots_available
|
session[:id] = @site.id
|
||||||
|
|
||||||
if @server.nil?
|
|
||||||
raise 'no slots available'
|
|
||||||
end
|
|
||||||
|
|
||||||
@site.server = @server
|
|
||||||
@site.save
|
|
||||||
|
|
||||||
session[:username] = @site.username
|
|
||||||
redirect '/dashboard'
|
redirect '/dashboard'
|
||||||
else
|
else
|
||||||
slim :'/new'
|
slim :'/new'
|
||||||
|
@ -38,8 +39,10 @@ post '/create' do
|
||||||
end
|
end
|
||||||
|
|
||||||
post '/signin' do
|
post '/signin' do
|
||||||
|
dashboard_if_signed_in
|
||||||
if Site.valid_login? params[:username], params[:password]
|
if Site.valid_login? params[:username], params[:password]
|
||||||
session[:username] = params[:username]
|
site = Site[username: params[:username]]
|
||||||
|
session[:id] = site.id
|
||||||
redirect '/dashboard'
|
redirect '/dashboard'
|
||||||
else
|
else
|
||||||
flash[:error] = 'Invalid login.'
|
flash[:error] = 'Invalid login.'
|
||||||
|
@ -49,8 +52,7 @@ end
|
||||||
|
|
||||||
get '/signout' do
|
get '/signout' do
|
||||||
require_login
|
require_login
|
||||||
session[:username] = nil
|
session[:id] = nil
|
||||||
session[:timezone] = nil
|
|
||||||
redirect '/'
|
redirect '/'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -63,5 +65,9 @@ def require_login
|
||||||
end
|
end
|
||||||
|
|
||||||
def signed_in?
|
def signed_in?
|
||||||
!session[:username].nil?
|
!session[:id].nil?
|
||||||
|
end
|
||||||
|
|
||||||
|
def current_site
|
||||||
|
@site ||= Site[id: session[:id]]
|
||||||
end
|
end
|
|
@ -1,2 +1,3 @@
|
||||||
development:
|
development:
|
||||||
database: 'postgres://localhost/neocities'
|
database: 'postgres://localhost/neocities'
|
||||||
|
session_secret: SETSOMETHINGHERE
|
|
@ -1,7 +1,8 @@
|
||||||
Sequel.migration do
|
Sequel.migration do
|
||||||
up {
|
up {
|
||||||
DB.create_table! :sites do
|
DB.create_table! :sites do
|
||||||
String :username, primary_key: true
|
primary_key :id
|
||||||
|
String :username
|
||||||
String :email
|
String :email
|
||||||
String :password
|
String :password
|
||||||
Integer :server_id
|
Integer :server_id
|
||||||
|
|
|
@ -3,7 +3,6 @@ Sequel.migration do
|
||||||
DB.create_table! :tags do
|
DB.create_table! :tags do
|
||||||
primary_key :id
|
primary_key :id
|
||||||
String :name
|
String :name
|
||||||
DateTime :created_at
|
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
12
migrations/004_create_sites_tags.rb
Normal file
12
migrations/004_create_sites_tags.rb
Normal 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
|
|
@ -1,8 +1,7 @@
|
||||||
class Site < Sequel::Model
|
class Site < Sequel::Model
|
||||||
MINIMUM_PASSWORD_LENGTH = 5
|
MINIMUM_PASSWORD_LENGTH = 5
|
||||||
|
|
||||||
unrestrict_primary_key
|
|
||||||
many_to_one :server
|
many_to_one :server
|
||||||
|
many_to_many :tags
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
def valid_login?(username, plaintext)
|
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)
|
values[:password] = BCrypt::Password.create plaintext, cost: (self.class.bcrypt_cost || BCrypt::Engine::DEFAULT_COST)
|
||||||
end
|
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
|
def validate
|
||||||
super
|
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.'
|
errors.add :username, 'A valid username is required.'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
3
models/tag.rb
Normal file
3
models/tag.rb
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
class Tag < Sequel::Model
|
||||||
|
many_to_many :sites
|
||||||
|
end
|
|
@ -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
|
4
tests/fabricators/site_fabricator.rb
Normal file
4
tests/fabricators/site_fabricator.rb
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
Fabricator(:site) do
|
||||||
|
username { Faker::Internet.email }
|
||||||
|
password { 'abcde' }
|
||||||
|
end
|
|
@ -1,4 +1,4 @@
|
||||||
.row
|
.row
|
||||||
.span12
|
.span12
|
||||||
|
|
||||||
h1 Dashboard!
|
h1 Dashboard
|
|
@ -19,7 +19,7 @@ html
|
||||||
a.brand href="/" NeoCities
|
a.brand href="/" NeoCities
|
||||||
ul.nav.pull-right
|
ul.nav.pull-right
|
||||||
- if signed_in?
|
- 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
|
li: a href="/signout" style="color: #B94A48" Signout
|
||||||
- else
|
- else
|
||||||
li: a href="/signin" <b>Sign in</b>
|
li: a href="/signin" <b>Sign in</b>
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
.row
|
.row
|
||||||
.span8.offset2
|
.span8.offset2
|
||||||
.alert.alert-block.alert-error
|
.alert.alert-block.alert-error
|
||||||
p Please correct the following errors:
|
p There were errors creating your home page:
|
||||||
- @site.errors.each do |error|
|
- @site.errors.each do |error|
|
||||||
p = error.last.first
|
p = error.last.first
|
||||||
|
|
||||||
|
@ -14,17 +14,17 @@
|
||||||
|
|
||||||
.row
|
.row
|
||||||
.span6
|
.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
|
.row
|
||||||
.span1
|
.span1
|
||||||
h5 Username
|
h5 Username
|
||||||
.span6
|
.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
|
.row
|
||||||
.span6
|
.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
|
.row
|
||||||
.span1
|
.span1
|
||||||
|
@ -34,26 +34,30 @@
|
||||||
|
|
||||||
.row
|
.row
|
||||||
.span6
|
.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
|
.row
|
||||||
.span1
|
.span1
|
||||||
h5 Email
|
h5 Email
|
||||||
.span6
|
.span6
|
||||||
input name="email" type="text" placeholder="youremail@example.com"
|
input name="email" type="text" placeholder="youremail@example.com" value="#{@site.email}"
|
||||||
|
|
||||||
|
|
||||||
.row
|
.row
|
||||||
.span6
|
.span6
|
||||||
p
|
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
|
.row
|
||||||
.span1
|
.span1
|
||||||
h5 Tags
|
h5 Tags
|
||||||
.span6
|
.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"
|
.row style="margin-top: 30px"
|
||||||
.span3.offset1
|
.span3.offset1
|
||||||
input.btn.btn-success.btn-large type="submit" value="Create Home Page"
|
input.btn.btn-success.btn-large type="submit" value="Create Home Page"
|
Loading…
Add table
Reference in a new issue