Skip to content

Commit

Permalink
- initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
hkalexling committed Feb 11, 2020
0 parents commit 3c2b054
Show file tree
Hide file tree
Showing 15 changed files with 324 additions and 0 deletions.
9 changes: 9 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
root = true

[*.cr]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/docs/
/lib/
/bin/
/.shards/
*.dwarf
6 changes: 6 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
language: crystal

# Uncomment the following if you'd like Travis to run specs and check code formatting
# script:
# - crystal spec
# - crystal tool format --check
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
The MIT License (MIT)

Copyright (c) 2020 Alex Ling

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# mango

TODO: Write a description here

## Installation

TODO: Write installation instructions here

## Usage

TODO: Write usage instructions here

## Development

TODO: Write development instructions here

## Contributing

1. Fork it (<https://github.com/your-github-user/mango/fork>)
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`)
4. Push to the branch (`git push origin my-new-feature`)
5. Create a new Pull Request

## Contributors

- [Alex Ling](https://github.com/your-github-user) - creator and maintainer
Binary file added mango
Binary file not shown.
30 changes: 30 additions & 0 deletions shard.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
version: 1.0
shards:
db:
github: crystal-lang/crystal-db
version: 0.8.0

exception_page:
github: crystal-loot/exception_page
version: 0.1.2

kemal:
github: kemalcr/kemal
version: 0.26.1

kemal-basic-auth:
github: kemalcr/kemal-basic-auth
version: 0.2.0

kilt:
github: jeromegn/kilt
version: 0.4.0

radix:
github: luislavena/radix
version: 0.3.9

sqlite3:
github: crystal-lang/crystal-sqlite3
version: 0.15.0

21 changes: 21 additions & 0 deletions shard.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: mango
version: 0.1.0

authors:
- Alex Ling <[email protected]>

targets:
mango:
main: src/mango.cr

crystal: 0.32.1

license: MIT

dependencies:
kemal:
github: kemalcr/kemal
kemal-basic-auth:
github: kemalcr/kemal-basic-auth
sqlite3:
github: crystal-lang/crystal-sqlite3
9 changes: 9 additions & 0 deletions spec/mango_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
require "./spec_helper"

describe Mango do
# TODO: Write tests

it "works" do
false.should eq(true)
end
end
2 changes: 2 additions & 0 deletions spec/spec_helper.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
require "spec"
require "../src/mango"
15 changes: 15 additions & 0 deletions src/auth_handler.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
require "kemal"

class AuthHandler < Kemal::Handler
exclude ["/login"]
def call(env)
return call_next(env) if exclude_match?(env)
my_cookie = HTTP::Cookie.new(
name: "Example",
value: "KemalCR"
)
env.response.cookies << my_cookie

pp env.request.cookies
end
end
37 changes: 37 additions & 0 deletions src/config.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
require "yaml"
require "uuid"
require "base64"

class Config
include YAML::Serializable

@[YAML::Field(key: "port")]
property port = 9000

@[YAML::Field(key: "library_path")]
property library_path = File.expand_path "~/mango-library", home: true

@[YAML::Field(key: "db_path")]
property db_path = File.expand_path "~/mango-library/mango.db", home: true

def self.load
cfg_path = File.expand_path "~/.config/mango/config.yml", home: true
if File.exists? cfg_path
return self.from_yaml File.read cfg_path
end
puts "The config file #{cfg_path} does not exist." \
"Do you want mango to dump the default config there? [Y/n]"
input = gets
if !input.nil? && input.downcase == "n"
abort "Aborting..."
end
default = self.allocate
cfg_dir = File.dirname cfg_path
unless Dir.exists? cfg_dir
Dir.mkdir_p cfg_dir
end
File.write cfg_path, default.to_yaml
puts "The config file has been created at #{cfg_path}."
default
end
end
44 changes: 44 additions & 0 deletions src/library.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
require "zip"

class Entry
property zip_path : String
property title : String
property size : String

def initialize(path : String)
@zip_path = path
@title = File.basename path, ".zip"
@size = (File.size path).humanize_bytes
end
end

class Title
property dir : String
property entries : Array(Entry)
property title : String

def initialize(dir : String)
@dir = dir
@title = File.basename dir
@entries = (Dir.entries dir)
.select! { |path| (File.extname path) == ".zip" }
.map { |path| Entry.new File.join dir, path }
.sort { |a, b| a.title <=> b.title }
end
end

class Library
property dir : String
property titles : Array(Title)

def initialize(dir : String)
@dir = dir
unless Dir.exists? dir
abort "ERROR: The library directory #{dir} does not exist"
end
@titles = (Dir.entries dir)
.select! { |path| File.directory? File.join dir, path }
.map { |path| Title.new File.join dir, path }
.select! { |title| !title.entries.empty? }
end
end
25 changes: 25 additions & 0 deletions src/mango.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
require "kemal"
require "./config"
require "./library"
require "./storage"
require "./auth_handler"

config = Config.load

library = Library.new config.library_path

storage = Storage.new config.db_path

get "/" do
"Hello World!"
end

# APIs
get "/api/test" do |env|
"Hello!"
end

add_handler AuthHandler.new

Kemal.config.port = config.port
Kemal.run
73 changes: 73 additions & 0 deletions src/storage.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
require "sqlite3"
require "crypto/bcrypt"
require "uuid"
require "base64"

def hash_password(pw)
Crypto::Bcrypt::Password.create(pw).to_s
end

def verify_password(hash, pw)
(Crypto::Bcrypt::Password.new hash).verify pw
end

def random_str()
Base64.strict_encode UUID.random().to_s
end

class Storage
property path : String

def initialize(path)
@path = path
DB.open "sqlite3://#{path}" do |db|
begin
db.exec "create table users" \
"(username text, password text, token text, admin integer)"
rescue e : SQLite3::Exception | DB::Error
unless e.message == "table users already exists"
raise e
end
else
db.exec "create unique index username_idx on users (username)"
db.exec "create unique index token_idx on users (token)"
random_pw = random_str
hash = hash_password random_pw
db.exec "insert into users values (?, ?, ?, ?)",
"admin", hash, "", 1
puts "Initial user created. You can log in with " \
"#{{"username" => "admin", "password" => random_pw}}"
end
end
end

def verify_user(username, password)
DB.open "sqlite3://#{@path}" do |db|
begin
hash = db.query_one "select password from users where " \
"username = (?)", username, as: String
unless verify_password hash, password
return nil
end
token = random_str
db.exec "update users set token = (?) where username = (?)",
token, username
return token
rescue e : SQLite3::Exception | DB::Error
return nil
end
end
end

def verify_token(token)
DB.open "sqlite3://#{@path}" do |db|
begin
username = db.query_one "select username from users where " \
"token = (?)", token, as: String
return username
rescue e : SQLite3::Exception | DB::Error
return nil
end
end
end
end

0 comments on commit 3c2b054

Please sign in to comment.