mirror of
https://github.com/neocities/neocities.git
synced 2025-04-24 17:22:35 +02:00
Initial checkin of project. Creates sites and goes to dashboard
This commit is contained in:
parent
d1d1d61f18
commit
b2bdf32275
26 changed files with 540 additions and 0 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -16,3 +16,5 @@ tmp
|
||||||
.yardoc
|
.yardoc
|
||||||
_yardoc
|
_yardoc
|
||||||
doc/
|
doc/
|
||||||
|
tests/coverage
|
||||||
|
config.yml
|
||||||
|
|
38
Gemfile
Normal file
38
Gemfile
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
source 'https://rubygems.org'
|
||||||
|
|
||||||
|
gem 'sinatra'
|
||||||
|
gem 'redis'
|
||||||
|
gem 'sequel'
|
||||||
|
gem 'slim'
|
||||||
|
gem 'bcrypt-ruby', require: 'bcrypt'
|
||||||
|
gem 'sinatra-flash', require: 'sinatra/flash'
|
||||||
|
gem 'puma', require: nil
|
||||||
|
|
||||||
|
platform :mri do
|
||||||
|
gem 'pg'
|
||||||
|
gem 'hiredis'
|
||||||
|
|
||||||
|
group :development, :test do
|
||||||
|
gem 'pry'
|
||||||
|
gem 'pry-debugger'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
group :development do
|
||||||
|
gem 'shotgun', require: nil
|
||||||
|
end
|
||||||
|
|
||||||
|
group :test do
|
||||||
|
gem 'faker'
|
||||||
|
gem 'fabrication', require: 'fabrication'
|
||||||
|
gem 'minitest'
|
||||||
|
gem 'minitest-reporters', require: 'minitest/reporters'
|
||||||
|
gem 'rack-test', require: 'rack/test'
|
||||||
|
gem 'webmock'
|
||||||
|
gem 'mocha', require: nil
|
||||||
|
gem 'rake', require: nil
|
||||||
|
|
||||||
|
platform :mri do
|
||||||
|
gem 'simplecov', require: nil
|
||||||
|
end
|
||||||
|
end
|
101
Gemfile.lock
Normal file
101
Gemfile.lock
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
GEM
|
||||||
|
remote: https://rubygems.org/
|
||||||
|
specs:
|
||||||
|
addressable (2.3.4)
|
||||||
|
ansi (1.4.3)
|
||||||
|
bcrypt-ruby (3.0.1)
|
||||||
|
builder (3.2.0)
|
||||||
|
coderay (1.0.9)
|
||||||
|
columnize (0.3.6)
|
||||||
|
crack (0.3.2)
|
||||||
|
debugger (1.6.0)
|
||||||
|
columnize (>= 0.3.1)
|
||||||
|
debugger-linecache (~> 1.2.0)
|
||||||
|
debugger-ruby_core_source (~> 1.2.1)
|
||||||
|
debugger-linecache (1.2.0)
|
||||||
|
debugger-ruby_core_source (1.2.2)
|
||||||
|
fabrication (2.7.1)
|
||||||
|
faker (1.1.2)
|
||||||
|
i18n (~> 0.5)
|
||||||
|
hashie (2.0.5)
|
||||||
|
hiredis (0.4.5)
|
||||||
|
i18n (0.6.4)
|
||||||
|
metaclass (0.0.1)
|
||||||
|
method_source (0.8.1)
|
||||||
|
minitest (4.7.4)
|
||||||
|
minitest-reporters (0.14.19)
|
||||||
|
ansi
|
||||||
|
builder
|
||||||
|
minitest (>= 2.12, < 5.0)
|
||||||
|
powerbar
|
||||||
|
mocha (0.14.0)
|
||||||
|
metaclass (~> 0.0.1)
|
||||||
|
multi_json (1.7.3)
|
||||||
|
pg (0.15.1)
|
||||||
|
powerbar (1.0.11)
|
||||||
|
ansi (~> 1.4.0)
|
||||||
|
hashie (>= 1.1.0)
|
||||||
|
pry (0.9.12.2)
|
||||||
|
coderay (~> 1.0.5)
|
||||||
|
method_source (~> 0.8)
|
||||||
|
slop (~> 3.4)
|
||||||
|
pry-debugger (0.2.2)
|
||||||
|
debugger (~> 1.3)
|
||||||
|
pry (~> 0.9.10)
|
||||||
|
puma (2.0.1)
|
||||||
|
rack (>= 1.1, < 2.0)
|
||||||
|
rack (1.5.2)
|
||||||
|
rack-protection (1.5.0)
|
||||||
|
rack
|
||||||
|
rack-test (0.6.2)
|
||||||
|
rack (>= 1.0)
|
||||||
|
rake (10.0.4)
|
||||||
|
redis (3.0.4)
|
||||||
|
sequel (3.47.0)
|
||||||
|
shotgun (0.9)
|
||||||
|
rack (>= 1.0)
|
||||||
|
simplecov (0.7.1)
|
||||||
|
multi_json (~> 1.0)
|
||||||
|
simplecov-html (~> 0.7.1)
|
||||||
|
simplecov-html (0.7.1)
|
||||||
|
sinatra (1.4.2)
|
||||||
|
rack (~> 1.5, >= 1.5.2)
|
||||||
|
rack-protection (~> 1.4)
|
||||||
|
tilt (~> 1.3, >= 1.3.4)
|
||||||
|
sinatra-flash (0.3.0)
|
||||||
|
sinatra (>= 1.0.0)
|
||||||
|
slim (1.3.9)
|
||||||
|
temple (~> 0.6.3)
|
||||||
|
tilt (~> 1.3, >= 1.3.3)
|
||||||
|
slop (3.4.5)
|
||||||
|
temple (0.6.5)
|
||||||
|
tilt (1.4.1)
|
||||||
|
webmock (1.11.0)
|
||||||
|
addressable (>= 2.2.7)
|
||||||
|
crack (>= 0.3.2)
|
||||||
|
|
||||||
|
PLATFORMS
|
||||||
|
ruby
|
||||||
|
|
||||||
|
DEPENDENCIES
|
||||||
|
bcrypt-ruby
|
||||||
|
fabrication
|
||||||
|
faker
|
||||||
|
hiredis
|
||||||
|
minitest
|
||||||
|
minitest-reporters
|
||||||
|
mocha
|
||||||
|
pg
|
||||||
|
pry
|
||||||
|
pry-debugger
|
||||||
|
puma
|
||||||
|
rack-test
|
||||||
|
rake
|
||||||
|
redis
|
||||||
|
sequel
|
||||||
|
shotgun
|
||||||
|
simplecov
|
||||||
|
sinatra
|
||||||
|
sinatra-flash
|
||||||
|
slim
|
||||||
|
webmock
|
10
Rakefile
Normal file
10
Rakefile
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
require "rake/testtask"
|
||||||
|
|
||||||
|
desc "Run all tests"
|
||||||
|
Rake::TestTask.new do |t|
|
||||||
|
t.libs << "spec"
|
||||||
|
t.test_files = FileList['tests/*_test.rb']
|
||||||
|
t.verbose = true
|
||||||
|
end
|
||||||
|
|
||||||
|
task :default => :test
|
63
app.rb
Normal file
63
app.rb
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
require './environment.rb'
|
||||||
|
|
||||||
|
get '/' do
|
||||||
|
slim :index
|
||||||
|
end
|
||||||
|
|
||||||
|
get '/new' do
|
||||||
|
@site = Site.new
|
||||||
|
slim :'new'
|
||||||
|
end
|
||||||
|
|
||||||
|
get '/dashboard' do
|
||||||
|
slim :'dashboard'
|
||||||
|
end
|
||||||
|
|
||||||
|
post '/create' do
|
||||||
|
@site = Site.new username: params[:username], password: params[:password], email: params[:email]
|
||||||
|
if @site.valid?
|
||||||
|
|
||||||
|
@server = Server.with_slots_available
|
||||||
|
|
||||||
|
if @server.nil?
|
||||||
|
raise 'no slots available'
|
||||||
|
end
|
||||||
|
|
||||||
|
@site.server = @server
|
||||||
|
@site.save
|
||||||
|
|
||||||
|
session[:username] = @site.username
|
||||||
|
redirect '/dashboard'
|
||||||
|
else
|
||||||
|
slim :'/new'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
post '/signin' do
|
||||||
|
if Site.valid_login? params[:username], params[:password]
|
||||||
|
session[:username] = params[:username]
|
||||||
|
redirect '/dashboard'
|
||||||
|
else
|
||||||
|
flash[:error] = 'Invalid login.'
|
||||||
|
redirect '/'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
get '/signout' do
|
||||||
|
require_login
|
||||||
|
session[:username] = nil
|
||||||
|
session[:timezone] = nil
|
||||||
|
redirect '/'
|
||||||
|
end
|
||||||
|
|
||||||
|
def dashboard_if_signed_in
|
||||||
|
redirect '/dashboard' if signed_in?
|
||||||
|
end
|
||||||
|
|
||||||
|
def require_login
|
||||||
|
redirect '/' unless signed_in?
|
||||||
|
end
|
||||||
|
|
||||||
|
def signed_in?
|
||||||
|
!session[:username].nil?
|
||||||
|
end
|
3
config.ru
Normal file
3
config.ru
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
require './app.rb'
|
||||||
|
|
||||||
|
map('/') { run Sinatra::Application }
|
2
config.yml.template
Normal file
2
config.yml.template
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
development:
|
||||||
|
database: 'postgres://localhost/neocities'
|
38
environment.rb
Normal file
38
environment.rb
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
ENV['RACK_ENV'] ||= 'development'
|
||||||
|
ENV['TZ'] = 'UTC'
|
||||||
|
DIR_ROOT = File.expand_path File.dirname(__FILE__)
|
||||||
|
Encoding.default_internal = 'UTF-8'
|
||||||
|
|
||||||
|
require 'yaml'
|
||||||
|
require 'json'
|
||||||
|
require 'logger'
|
||||||
|
|
||||||
|
Bundler.require
|
||||||
|
Bundler.require :development if ENV['RACK_ENV'] == 'development'
|
||||||
|
|
||||||
|
$config = YAML.load_file(File.join(DIR_ROOT, 'config.yml'))[ENV['RACK_ENV']]
|
||||||
|
|
||||||
|
DB = Sequel.connect $config['database']
|
||||||
|
|
||||||
|
Dir.glob('workers/*.rb').each {|w| require File.join(DIR_ROOT, "/#{w}") }
|
||||||
|
|
||||||
|
if defined?(Pry)
|
||||||
|
Pry.commands.alias_command 'c', 'continue'
|
||||||
|
Pry.commands.alias_command 's', 'step'
|
||||||
|
Pry.commands.alias_command 'n', 'next'
|
||||||
|
Pry.commands.alias_command 'f', 'finish'
|
||||||
|
end
|
||||||
|
|
||||||
|
Sequel.datetime_class = Time
|
||||||
|
Sequel.extension :pagination
|
||||||
|
Sequel.extension :migration
|
||||||
|
Sequel::Model.plugin :validation_helpers
|
||||||
|
Sequel::Model.plugin :force_encoding, 'UTF-8'
|
||||||
|
Sequel::Model.plugin :timestamps, create: :created_at, update: :updated_at
|
||||||
|
Sequel::Model.plugin :defaults_setter
|
||||||
|
Sequel.default_timezone = 'UTC'
|
||||||
|
Sequel::Migrator.apply DB, './migrations'
|
||||||
|
|
||||||
|
Dir.glob('models/*.rb').each {|m| require File.join(DIR_ROOT, "#{m}") }
|
||||||
|
|
||||||
|
DB.loggers << Logger.new(STDOUT) if ENV['RACK_ENV'] == 'development'
|
16
migrations/001_create_sites.rb
Normal file
16
migrations/001_create_sites.rb
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
Sequel.migration do
|
||||||
|
up {
|
||||||
|
DB.create_table! :sites do
|
||||||
|
String :username, primary_key: true
|
||||||
|
String :email
|
||||||
|
String :password
|
||||||
|
Integer :server_id
|
||||||
|
DateTime :created_at
|
||||||
|
DateTime :updated_at
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
down {
|
||||||
|
DB.drop_table :sites
|
||||||
|
}
|
||||||
|
end
|
15
migrations/002_create_servers.rb
Normal file
15
migrations/002_create_servers.rb
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
Sequel.migration do
|
||||||
|
up {
|
||||||
|
DB.create_table! :servers do
|
||||||
|
primary_key :id
|
||||||
|
String :ip
|
||||||
|
Integer :slots_available
|
||||||
|
DateTime :created_at
|
||||||
|
DateTime :updated_at
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
down {
|
||||||
|
DB.drop_table :servers
|
||||||
|
}
|
||||||
|
end
|
7
models/server.rb
Normal file
7
models/server.rb
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
class Server < Sequel::Model
|
||||||
|
one_to_many :sites
|
||||||
|
|
||||||
|
def self.with_slots_available
|
||||||
|
where{slots_available > 0}.first
|
||||||
|
end
|
||||||
|
end
|
50
models/site.rb
Normal file
50
models/site.rb
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
class Site < Sequel::Model
|
||||||
|
MINIMUM_PASSWORD_LENGTH = 5
|
||||||
|
|
||||||
|
unrestrict_primary_key
|
||||||
|
many_to_one :server
|
||||||
|
|
||||||
|
class << self
|
||||||
|
def valid_login?(username, plaintext)
|
||||||
|
site = self[username: username]
|
||||||
|
return false if site.nil?
|
||||||
|
site.valid_password? plaintext
|
||||||
|
end
|
||||||
|
|
||||||
|
def bcrypt_cost
|
||||||
|
@bcrypt_cost
|
||||||
|
end
|
||||||
|
|
||||||
|
def bcrypt_cost=(cost)
|
||||||
|
@bcrypt_cost = cost
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def valid_password?(plaintext)
|
||||||
|
BCrypt::Password.new(values[:password]) == plaintext
|
||||||
|
end
|
||||||
|
|
||||||
|
def password=(plaintext)
|
||||||
|
@password_length = plaintext.nil? ? 0 : plaintext.length
|
||||||
|
@password_plaintext = plaintext
|
||||||
|
values[:password] = BCrypt::Password.create plaintext, cost: (self.class.bcrypt_cost || BCrypt::Engine::DEFAULT_COST)
|
||||||
|
end
|
||||||
|
|
||||||
|
def validate
|
||||||
|
super
|
||||||
|
|
||||||
|
if values[:username].nil? || values[:username].empty?
|
||||||
|
errors.add :username, 'valid username is required'
|
||||||
|
end
|
||||||
|
|
||||||
|
# Check for existing user
|
||||||
|
user = self.class.select(:username).filter(username: values[:username]).first
|
||||||
|
if !user.nil? && (user.id != values[:id])
|
||||||
|
errors.add :username, 'this username is already taken'
|
||||||
|
end
|
||||||
|
|
||||||
|
if values[:password].nil? || (@password_length && @password_length < MINIMUM_PASSWORD_LENGTH)
|
||||||
|
errors.add :password, "password must be at least #{MINIMUM_PASSWORD_LENGTH} characters"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
9
public/css/bootstrap-responsive.min.css
vendored
Normal file
9
public/css/bootstrap-responsive.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
9
public/css/bootstrap.min.css
vendored
Normal file
9
public/css/bootstrap.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
33
public/css/font-awesome.min.css
vendored
Normal file
33
public/css/font-awesome.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
0
public/css/styles.css
Normal file
0
public/css/styles.css
Normal file
BIN
public/img/glyphicons-halflings-white.png
Normal file
BIN
public/img/glyphicons-halflings-white.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.6 KiB |
BIN
public/img/glyphicons-halflings.png
Normal file
BIN
public/img/glyphicons-halflings.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
6
public/js/bootstrap.min.js
vendored
Normal file
6
public/js/bootstrap.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
5
public/js/jquery.min.js
vendored
Normal file
5
public/js/jquery.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
0
tests/app_test.rb
Normal file
0
tests/app_test.rb
Normal file
34
tests/environment.rb
Normal file
34
tests/environment.rb
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
ENV['RACK_ENV'] = 'test'
|
||||||
|
raise 'Forget it.' if ENV['RACK_ENV'] == 'production'
|
||||||
|
|
||||||
|
require 'simplecov'
|
||||||
|
SimpleCov.coverage_dir File.join('tests', 'coverage')
|
||||||
|
SimpleCov.start do
|
||||||
|
add_filter "/migrations/"
|
||||||
|
end
|
||||||
|
|
||||||
|
require './environment'
|
||||||
|
require 'webmock'
|
||||||
|
include WebMock::API
|
||||||
|
require './app'
|
||||||
|
|
||||||
|
Bundler.require :test
|
||||||
|
|
||||||
|
#require 'minitest/pride'
|
||||||
|
require 'minitest/autorun'
|
||||||
|
require 'sidekiq/testing/inline'
|
||||||
|
|
||||||
|
Account.bcrypt_cost = BCrypt::Engine::MIN_COST
|
||||||
|
|
||||||
|
MiniTest::Reporters.use! MiniTest::Reporters::SpecReporter.new
|
||||||
|
|
||||||
|
# Bootstrap the database
|
||||||
|
Sequel.extension :migration
|
||||||
|
|
||||||
|
Sequel::Migrator.apply DB, './migrations', 0
|
||||||
|
Sequel::Migrator.apply DB, './migrations'
|
||||||
|
|
||||||
|
Fabrication.configure do |config|
|
||||||
|
config.fabricator_path = 'tests/fabricators'
|
||||||
|
config.path_prefix = DIR_ROOT
|
||||||
|
end
|
4
views/dashboard.slim
Normal file
4
views/dashboard.slim
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
.row
|
||||||
|
.span12
|
||||||
|
|
||||||
|
h1 Dashboard!
|
29
views/index.slim
Normal file
29
views/index.slim
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
.row
|
||||||
|
.span12
|
||||||
|
|
||||||
|
h1 Your free home page is waiting.. once again.
|
||||||
|
|
||||||
|
h2 NeoCities allows you to create your own free home page!
|
||||||
|
|
||||||
|
div style="margin-top: 30px"
|
||||||
|
|
||||||
|
table.table.table-bordered
|
||||||
|
tbody
|
||||||
|
tr
|
||||||
|
td: b What kind of look and feel can my home page have?
|
||||||
|
td Anything you want! It's just HTML. It's your home page, your vision. You can make it as straight or crazy as you want. You're the artist, you're in control. Make a web site about you, or make it about cats that play Dungeons and Dragons, or make a Daft Punk Fan Site, or a Daft Punk Sucks Site.
|
||||||
|
tr
|
||||||
|
td: b What information can I put on it?
|
||||||
|
td Anything you wan't, as long as it's not illegal to host it in the United States. We don't care if it offends somebody, or you remain anonymous.
|
||||||
|
tr
|
||||||
|
td: b Do I own my content?
|
||||||
|
td Yes, you do. It's yours. We're just giving you a place to host it.
|
||||||
|
tr
|
||||||
|
td: b How much space do I get?
|
||||||
|
td 5 Megabytes. You can have HTML, text, and images. That's it.
|
||||||
|
tr
|
||||||
|
td: b How are you paying for this?
|
||||||
|
td Hopefully with donations. If you feel this is a useful service, help us pay the bills to run it. <a href="/donate">Click here to contribute!</a>
|
||||||
|
div.text-center
|
||||||
|
h1: a href="/new" JOIN NOW
|
||||||
|
|
28
views/layout.slim
Normal file
28
views/layout.slim
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
doctype html
|
||||||
|
html
|
||||||
|
head
|
||||||
|
title NeoCities #{@title.nil? ? '' : " - #{@title}"}
|
||||||
|
link href="/favicon.ico" rel="icon" type="image/x-icon"
|
||||||
|
link href="/css/bootstrap.min.css" rel="stylesheet"
|
||||||
|
link href="/css/bootstrap-responsive.min.css" rel="stylesheet"
|
||||||
|
link href="/css/font-awesome.min.css" rel="stylesheet"
|
||||||
|
link href="/css/styles.css" rel="stylesheet"
|
||||||
|
script src="/js/jquery.min.js"
|
||||||
|
body
|
||||||
|
a id="top"
|
||||||
|
|
||||||
|
.container
|
||||||
|
.row
|
||||||
|
.span12
|
||||||
|
.navbar
|
||||||
|
.navbar-inner
|
||||||
|
a.brand href="/" NeoCities
|
||||||
|
|
||||||
|
- flash.keys.each do |key|
|
||||||
|
div class="alert alert-#{key}"
|
||||||
|
button.close type="button" data-dismiss="alert" ×
|
||||||
|
= flash[key]
|
||||||
|
container
|
||||||
|
== yield
|
||||||
|
|
||||||
|
script src="/js/bootstrap.min.js"
|
38
views/new.slim
Normal file
38
views/new.slim
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
.row
|
||||||
|
.span8.offset3
|
||||||
|
form method="POST" action="/create"
|
||||||
|
h2 Create a new Home Page
|
||||||
|
|
||||||
|
|
||||||
|
.row
|
||||||
|
.span6
|
||||||
|
p First, enter a username. This will be used to login to your site.
|
||||||
|
|
||||||
|
.row
|
||||||
|
.span1
|
||||||
|
h5 Username
|
||||||
|
.span6
|
||||||
|
input name="username" type="text" style="color: #000000" placeholder="bulbasaur1000"
|
||||||
|
|
||||||
|
.row
|
||||||
|
.span6
|
||||||
|
p Next, enter a password. This will be used to allow you to login. Minimum 5 characters.
|
||||||
|
|
||||||
|
.row
|
||||||
|
.span1
|
||||||
|
h5 Password
|
||||||
|
.span6
|
||||||
|
input name="password" type="password" style="color: #000000"
|
||||||
|
|
||||||
|
.row
|
||||||
|
.span6
|
||||||
|
p Now, you can enter an e-mail address if you want to be able to reset your password incase you lose it. This is optional, but recommended. <b>We will not give your email to anybody</b>.
|
||||||
|
.row
|
||||||
|
.span1
|
||||||
|
h5 Email
|
||||||
|
.span6
|
||||||
|
input name="email" type="text" style="color: #000000" placeholder="youremail@example.com"
|
||||||
|
|
||||||
|
.row style="margin-top: 30px"
|
||||||
|
.span3.offset1
|
||||||
|
input.btn.btn-success.btn-large type="submit" value="Create Home Page"
|
Loading…
Add table
Reference in a new issue