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
|
||||
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