diff --git a/src/Gemfile b/src/Gemfile deleted file mode 100644 index 6a8d04b..0000000 --- a/src/Gemfile +++ /dev/null @@ -1,23 +0,0 @@ -# frozen_string_literal: true - -source "https://rubygems.org" - -git_source(:github) { |repo_name| "https://github.com/#{repo_name}" } - -# gem "rails" - -gem "sinatra" -gem "sinatra-reloader" -gem "sqlite3" -gem "slim" -gem "sassc" -gem "colorize" -gem "bcrypt" - -gem "rmagick", "~> 4.2" - -gem "sinatra-flash", "~> 0.3.0" - -gem "fileutils", "~> 1.6" - -gem "webrick", "~> 1.7" diff --git a/src/Gemfile.lock b/src/Gemfile.lock deleted file mode 100644 index 9fffbd1..0000000 --- a/src/Gemfile.lock +++ /dev/null @@ -1,58 +0,0 @@ -GEM - remote: https://rubygems.org/ - specs: - bcrypt (3.1.17) - colorize (0.8.1) - ffi (1.15.5) - fileutils (1.6.0) - multi_json (1.15.0) - mustermann (1.1.1) - ruby2_keywords (~> 0.0.1) - rack (2.2.3) - rack-protection (2.2.0) - rack - rmagick (4.2.5) - ruby2_keywords (0.0.5) - sassc (2.4.0) - ffi (~> 1.9) - sinatra (2.2.0) - mustermann (~> 1.0) - rack (~> 2.2) - rack-protection (= 2.2.0) - tilt (~> 2.0) - sinatra-contrib (2.2.0) - multi_json - mustermann (~> 1.0) - rack-protection (= 2.2.0) - sinatra (= 2.2.0) - tilt (~> 2.0) - sinatra-flash (0.3.0) - sinatra (>= 1.0.0) - sinatra-reloader (1.0) - sinatra-contrib - slim (4.1.0) - temple (>= 0.7.6, < 0.9) - tilt (>= 2.0.6, < 2.1) - sqlite3 (1.4.2) - temple (0.8.2) - tilt (2.0.10) - webrick (1.7.0) - -PLATFORMS - x86_64-linux - -DEPENDENCIES - bcrypt - colorize - fileutils (~> 1.6) - rmagick (~> 4.2) - sassc - sinatra - sinatra-flash (~> 0.3.0) - sinatra-reloader - slim - sqlite3 - webrick (~> 1.7) - -BUNDLED WITH - 2.3.10 diff --git a/src/app.rb b/src/app.rb index d010a44..bed827c 100755 --- a/src/app.rb +++ b/src/app.rb @@ -38,6 +38,10 @@ not_found do serve :"404" end +# Return a flash with some error message +# @param [String] msg Message +# @param [Integer] status HTTP Status Code +# @param [String] ret Return route def auth_denied(msg=AUTH_ERRORS[:denied], status=403, ret=back) session[:status] = status session[:ret] = ret @@ -45,18 +49,26 @@ def auth_denied(msg=AUTH_ERRORS[:denied], status=403, ret=back) redirect ret end +# Wrapper for auth_denied when something goes wrong +# @see #auth_denied def no_go_away(ret=back) auth_denied "No! GO AWAY!", 403, ret end +# Banned response +# @see #auth_denied def banned(ret=back) auth_denied "You are banned!", 403, ret end +# Error response +# @see #auth_denied def error(ret=back) auth_denied "Internal server error.", 500, ret end +# Ratelimit response +# @see #auth_denied def ratelimit(delta_time, ret="/") auth_denied "You must wait #{format_time(delta_time)} to do that again!", 429, ret end diff --git a/src/lib/database.rb b/src/lib/database.rb index 94486f6..ae5747c 100644 --- a/src/lib/database.rb +++ b/src/lib/database.rb @@ -12,7 +12,8 @@ class EntityModel @data = data end - # Creates the table + # Initializes the table on startup + # @return [void] def self.init_table sql_file = "sql/tables/#{self.name}.sql" @@ -24,24 +25,38 @@ class EntityModel end end + # Generates a sql update query filter + # @param [Array] vars strings that are the sql table attributes + # @return [String] Query filter def self.gen_update_query(vars) # generates part of the update query string out = vars.join " = ?, " out += " = ?" end - def self.gen_insert_query(vars) # generates part of the insert query string + # Generates a SQL insert query filter + # @param [Array] vars strings that are the SQL table attributes + # @return [String] Query filter + def self.gen_insert_query(vars) entstr = "(#{vars.join ", "})" valstr = "(#{(["?"] * vars.length).join ", "})" return entstr, valstr end + # Applies a filter to a query + # @param [String] query The query + # @param [String] filter The filter + # @return [String] Full query SQL string with filter def self.apply_filter(query, filter) if filter != "" then query += " WHERE #{filter}" end query end - def self.query(q, *args) # query table with query string + # Query the table with given SQL query string + # @param [String] q The query + # @param [Array] args Other arguments (such as variable values etc) + # @return [Hash] Query result + def self.query(q, *args) Console.debug("Running SQL -> #{q}", *args) begin db.execute( q, args ) @@ -51,6 +66,11 @@ class EntityModel end # Extended query that also returns database instance + # @see EntityModel#query + # @param [String] q The query + # @param [Array] args Other arguments (such as variable values etc) + # @return [SQLite3::Database] SQLite3 Database instance + # @return [Hash] Query result def self.equery(q, *args) Console.debug("Running extended SQL -> #{q}", *args) begin @@ -62,6 +82,11 @@ class EntityModel end end + # Get data from table with given filter + # @see EntityModel#query + # @param [String] attr The table attribute name + # @param [Array] args Other arguments (such as variable values etc) + # @return [Hash] Query result def self.get(attr, filter="", *args) # get data from table q = "SELECT #{attr} FROM #{self.name}" # create the query string q = apply_filter(q, filter) @@ -69,16 +94,32 @@ class EntityModel self.query q, *args # execute query end + # Update data in table with given filter + # @see EntityModel#equery + # @param [Hash] data Hash of new data + # @param [String] filter Query filter + # @param [Array] args Other arguments (such as variable values etc) + # @return [Hash] Query result def self.update(data, filter="", *args) # Updates the table with specified data hash q = "UPDATE #{self.name} SET #{self.gen_update_query(data.keys)}" q = apply_filter(q, filter) self.query(q, *data.values, *args) end + # Update data in table where id = something + # @see EntityModel#update + # @param [Integer] id Selected primary key + # @param [Hash] data Hash of new data + # @return [Hash] Query result def self.edit(id, data) self.update(data, "id=?", id) end + # Insert data in table + # @see EntityModel#equery + # @param [Hash] data Hash of new data + # @return [Integer] New primary key that was generated + # @return [String] Response string def self.insert(data) # Inserts new data into the table entstr, valstr = self.gen_insert_query data.keys begin @@ -90,41 +131,52 @@ class EntityModel return newid, resp end + # Delete entry with given filter + # @see EntityModel#query + # @param [String] filter Query filter + # @param [Array] args Variable values etc def self.delete(filter="", *args) q = "DELETE FROM #{self.name}" q = self.apply_filter(q, filter) self.query q, *args end - def self.set(attr, data, filter="") # slower but more lazy - if self.get(attr, filter).length > 0 then - self.update(data, filter) - else - self.insert(data, filter) - end - end - + # Checks if primary key exists in table + # @param [Integer] id The primary key + # @return [Bool] If it exists or not def self.exists?(id) resp = self.get "id", "id = ?", id resp.length > 0 end + # Find object by id + # @param [Integer] id Primary key + # @return [Object] Model object def self.find_by_id(id) data = self.get("*", "id = ?", id).first data && self.new(data) end + # Get all ids in table + # @param [String] filter Query filter + # @param [Array] args Other arguments (such as variable values etc) + # @return [Array] Array of all the primary keys def self.get_all_ids filter="", *args ids = self.get "id", *args ids.map! {|k, id| id.to_i} end + # Get all objects in table + # @param [String] filter Query filter + # @param [Array] args Other arguments (such as variable values etc) + # @return [Array] Array of all the primary keys def self.get_all filter="", *args data = self.get "*", filter, *args data && data.map! {|r| self.new(r)} end end +=begin class RelationModel < EntityModel # TODO: make this work def self.tables = nil @@ -138,3 +190,5 @@ class RelationModel < EntityModel # TODO: make this work end end end +=end + diff --git a/src/lib/db_models.rb b/src/lib/db_models.rb index 603e436..56986f3 100644 --- a/src/lib/db_models.rb +++ b/src/lib/db_models.rb @@ -13,14 +13,18 @@ class User < EntityModel @pw_hash = data["pw_hash"] end + # Get user avatar url def avatar return @avatar_url end + # Get all of the users auctions def auctions Auction.get_all "user_id = ?", @id end + # Get the most dominant roles name + # @return [String] Role name def role return Role.find_by_id( ROLES[:admin][:id] ).name if self.admin? @@ -32,47 +36,55 @@ class User < EntityModel return "" end + # Get all of the users role ids + # @see User#roles + # @return [Array] Array of all the primary keys for the roles def role_ids User_Role_relation.get_user_roles_ids @id end + # Get all of the users role ids + # @see User#role_ids + # @return [Array] Array of all the users role objects def roles User_Role_relation.get_user_roles @id end + # Gets the reputation enum for the user + # @return [Integer] Reputation score enum def rep_score return BAD_REP if @reputation < 0 return GOOD_REP if @reputation > 0 return NEUTRAL_REP end - def bio_html - md_parser = Redcarpet::Markdown.new(Redcarpet::Render::HTML) - md_parser.render @bio_text - end - + # Reputation text for the user + # @return [String] Reputation score string def reputation_text sign = @reputation > 0 ? "+" : "" return "#{sign}#{@reputation}" end + # Sets the users reputation to given value + # @see EntityModel#update + # @param [Integer] val The value def reputation=(val) val = val.clamp MIN_REP, MAX_REP @reputation = val User.update({reputation: val}, "id = ?", @id) end + # Sets the users balance + # @see EntityModel#update + # @param [Float] val The value def balance=(val) val = val >= 0 ? val : 0 @balance = val User.update({balance: val}, "id = ?", @id) end - def reputation=(val) - @reputation = val - User.update({reputation: val}, "id = ?", @id) - end - + # Updates the user credentials + # @see EntityModel#update def update_creds(data) # Validate input return false, SETTINGS_ERRORS[:name_len] unless data[:name].length.between?(MIN_NAME_LEN, MAX_NAME_LEN) @@ -86,12 +98,21 @@ class User < EntityModel return true, nil end - # Find user by email, same as above but for emails. + # Find user by email, same as EntityModel#find_by_id but for emails. + # @see EntityModel#query + # @see EntityModel#find_by_id def self.find_by_email(email) data = self.get("*", "email = ?", email).first data && User.new(data) end + # Verify user registration credentials + # @param [String] email The email + # @param [String] name The users name + # @param [String] password Password + # @param [String] password_confirm Password confirmation + # @return [Boolean] Failed? + # @return [String] Error message def self.validate_register_creds(email, name, password, password_confirm) # Field check check_all_fields = email != "" && name != "" && password != "" && password_confirm != "" @@ -117,12 +138,21 @@ class User < EntityModel return true, "" end + # Verify password + # @param [String] pw_hash Digested password + # @param [String] password Password + # @return [Boolean] Passwords are equal def self.validate_password(pw_hash, password) BCrypt::Password.new(pw_hash) == password end # Register a new user - # Returns: success?, data + # @param [String] email Email + # @param [String] name Users name + # @param [String] password Password + # @param [String] password_confirm Password confirm + # @return [Boolean] success? + # @return [String] Error string def self.register(email, name, password, password_confirm) check, errorstr = self.validate_register_creds(email, name, password, password_confirm) @@ -142,7 +172,10 @@ class User < EntityModel end # Log in user - # Returns: success?, user id + # @param [String] email Users email + # @param [String] password Users password + # @return [Boolean] Success? + # @return [Integer] Users id def self.login(email, password) user = self.find_by_email email # get the user info @@ -154,8 +187,8 @@ class User < EntityModel return true, user.id end - # Get a users flags - # Returns: bitmap int thingie + # Get a users combined flags + # @return [Integer Bitmap] A bitmap of all the flags def flags flags = 0 self.roles.each do |role| @@ -166,12 +199,15 @@ class User < EntityModel return flags end + # Check if user is an admin + # @return [Boolean] def admin? return self.flags[1] == 1 end - # Check if user has flags - # Returns: true or false depending whether the user has those flags + # Check if user is permitted with certian flag + # @param [Symbol] flag Flag symbol + # @return [Boolean] true or false depending whether the user has those flags def permitted?(flag, *other_flags) return true if self.admin? @@ -183,10 +219,14 @@ class User < EntityModel return self.flags & flag_mask == flag_mask end + # Check if user is banned + # @return [Boolean] def banned? return self.flags[ PERM_LEVELS.keys.index(:banned) ] == 1 end + # Set users "banned" status + # @return [String] Error/info string def banned=(b) if b then # Add the "banned" role @@ -208,6 +248,7 @@ class Role < EntityModel @flags = data["flags"] end + # Check if role has a flag def has_flag?(flag, *other_flags) flag_mask = PERM_LEVELS[flag] @@ -221,11 +262,18 @@ class Role < EntityModel return @flags & flag_mask == flag_mask # f AND m = m => flags exists end + # Find role by name + # @see EntityModel#find_by_id + # @return [Role] Role object def self.find_by_name(name) data = self.get("*", "name = ?", name).first data && Role.new(data) end + # Create a role + # @param [String] name Role name + # @param [Color String] color Role color in hex + # @param [Integer Bitmap] flags Flags bitmap def self.create(name, color="#ffffff", flags=0) return false, REGISTER_ERRORS[:name_len] unless name.length.between?(MIN_NAME_LEN, MAX_NAME_LEN) @@ -251,6 +299,10 @@ class User_Role_relation < EntityModel end end + # Give role to user + # @param [Integer] user_id User id + # @param [Integer] role_id Role id + # @see EntityModel#insert def self.give_role(user_id, role_id) user = User.find_by_id user_id @@ -263,6 +315,10 @@ class User_Role_relation < EntityModel end end + # Revoke role from user + # @param [Integer] user_id User id + # @param [Integer] role_id Role id + # @see EntityModel#delete def self.revoke_role(user_id, role_id) user = User.find_by_id user_id @@ -271,6 +327,10 @@ class User_Role_relation < EntityModel end end + # Gets users role ids in an array + # @see User_Role_relation#get_user_roles + # @param [Integer] user_id User id + # @return [Array] Role ids def self.get_user_roles_ids(user_id) ids = self.get "role_id", "user_id = ?", user_id ids.map! do |ent| @@ -278,6 +338,10 @@ class User_Role_relation < EntityModel end end + # Gets users roles in an array + # @see User_Role_relation#get_user_roles_ids + # @param [Integer] user_id User id + # @return [Array] Roles def self.get_user_roles(user_id) roleids = self.get_user_roles_ids user_id roles = roleids.map do |id| @@ -300,6 +364,9 @@ class Auction < EntityModel @end_time = data["end_time"].to_i end + # Validates auction params + # @return [Boolean] Success? + # @return [String] Error string def self.validate_ah(title, description, init_price, delta_time) return false, AUCTION_ERRORS[:titlelen] unless title.length.between?(MIN_TITLE_LEN, MAX_TITLE_LEN) return false, AUCTION_ERRORS[:initprice] unless init_price >= MIN_INIT_PRICE @@ -308,6 +375,14 @@ class Auction < EntityModel return true, "" end + # Creates an auction post + # @param [Integer] user_id Posters id + # @param [String] title + # @param [String] description + # @param [Float] init_price Initial price offering + # @param [Integer] delta_time Auction duration in seconds + # @see EntityModel#insert + # @see Auction#validate_ah def self.create(user_id, title, description, init_price, delta_time) # Validate the input check, errorstr = self.validate_ah(title, description, init_price, delta_time) @@ -330,6 +405,8 @@ class Auction < EntityModel self.insert data end + # Composes SQL query filters for the auction searching function + # @return [string] Query filters def self.compose_query_filters(title=nil, categories=nil, min_price=nil, max_price=nil, expired=nil) querystr = "SELECT * FROM Auction WHERE " filters = [] @@ -366,17 +443,28 @@ class Auction < EntityModel return querystr end + # Searches the database for related auctions that fit the params + # @param [String] title + # @param [Array] categories Category ids + # @param [Integer] min_price + # @param [Integer] max_price + # @param [Boolean] expired + # @return [Array] Array of auctions def self.search(title=nil, categories=nil, min_price=nil, max_price=nil, expired=nil) q = self.compose_query_filters title, categories, min_price, max_price, expired data = self.query(q) data.map! {|dat| self.new(dat)} end + # Checks if expired + # @return [Boolean] def self.expired?(id) ah = self.find_by_id id ah && ah.expired? end + # Edits auction title and description + # @see EntityModel#update def edit(title, description) return false, AUCTION_ERRORS[:titlelen] unless title.length.between?(MIN_TITLE_LEN, MAX_TITLE_LEN) return false, AUCTION_ERRORS[:desclen] unless description.length.between?(MIN_DESC_LEN, MAX_DESC_LEN) @@ -388,6 +476,7 @@ class Auction < EntityModel Auction.update data, "id = ?", @id end + # Deletes the auction def delete FileUtils.rm_rf("./public/auctions/#{@id}") # delete all images @@ -397,49 +486,74 @@ class Auction < EntityModel Bid.delete "auction_id = ?", @id end + # Auction poster object + # @return [User] def poster User.find_by_id @user_id end + # Auction images + # @return [Array] def images Image.get_relation @id end + # Auctions category ids + # @see Auction#categories + # @return [Array] Array of ids def category_ids data = Auction_Category_relation.get "category_id", "auction_id = ?", @id data && data.map! {|category| category["category_id"].to_i} end + # Auctions categories + # @see Auction#category_ids + # @return [Array] Array of categories def categories data = self.category_ids data && data.map! { |catid| Category.find_by_id catid} end + # @see Auction#expired? def expired? Time.now.to_i > @end_time end + # Time left + # @return [Integer] Time left in seconds def time_left @end_time - Time.now.to_i end + # Time left + # @return [String] Formatted time string def time_left_s left = self.time_left return format_time(left) end + # Get auctions bids + # @return [Array] Bids def bids Bid.get_bids(@id) end + # Place bid on auction + # @param [Integer] uid Bidders id (user) + # @param [Float] amount + # @param [String] message + # @see Bid#place def place_bid(uid, amount, message) Bid.place(@id, uid, amount, message) end + # Get the dominant bid object + # @return [Bid] def max_bid max_bid = self.bids.max_by {|bid| bid.amount} end + # Current bid def current_bid mbid = self.max_bid if mbid != nil then @@ -449,6 +563,7 @@ class Auction < EntityModel end end + # Minimum required new bid def min_new_bid max_bid = self.max_bid amount = max_bid.nil? ? @init_price : max_bid.amount @@ -467,35 +582,40 @@ class Bid < EntityModel @message = data["message"] end + # Gets auctions bids def self.get_bids(ahid) data = self.get "*", "auction_id = ?", ahid data && data.map! {|dat| self.new(dat)} end + # Get users bids def self.get_user_bids(uid) data = self.get "*", "user_id = ?", uid data && data.map! {|dat| self.new(dat)} end + # Users bids active amount def self.get_user_active_amount(uid) bids = self.get_user_bids uid return bids.sum {|bid| bid.amount} end + # How much more the users new bid is from their last bid + # @return [Float] def self.get_delta_amount(ahid, uid, amount) data = self.get "*", "auction_id = ? AND user_id = ?", ahid, uid if data and data.length > 0 then data.map! {|dat| self.new(dat)} max_bid = data.max_by {|bid| bid.amount} - p "sgiodfhgiodfhioghoi" - p data - p "sgiodfhgiodfhioghoi" return amount - max_bid.amount else return amount end end + # Validate a new bid + # @return [Boolean] Success? + # @return [String] Error message def self.validate_bid(ahid, uid, amount, message) ah = Auction.find_by_id ahid return false, "Invalid auction" unless ah.is_a? Auction @@ -506,6 +626,15 @@ class Bid < EntityModel return true, "" end + # Place a new bid + # @param [Integer] ahid Auction id + # @param [Integer] uid User id + # @param [Float] amount + # @param [String] message + # @see EntityModel#insert + # @see Bid#validate_bid + # @return [Boolean] Success? + # @return [String, Hash] Error message or insert query data def self.place(ahid, uid, amount, message) check, resp = self.validate_bid(ahid, uid, amount, message) if check then @@ -535,6 +664,8 @@ class Category < EntityModel @color = data["color"] end + # Create a new category + # @see EntityModel#insert def self.create(name, color) data = { name: name, @@ -552,6 +683,8 @@ class Auction_Category_relation < EntityModel @category_id = data["category_id"] end + # Get all auctions that have the specified category + # @return [Array] Auction ids def self.category_auction_ids(catid) ids = self.get "auction_id", "category_id = ?", catid ids && ids.map! {|id| id["auction_id"].to_i} @@ -568,6 +701,10 @@ class Image < EntityModel @url = data["url"] end + # Save an image to the DB and disk + # @param [Image Data] imgdata + # @param [Integer] ah_id Auction id + # @param [Integer] order Image order on the auction page def self.save(imgdata, ah_id, order) FileUtils.mkdir_p "./public/auctions/#{ah_id}" @@ -588,6 +725,8 @@ class Image < EntityModel end end + # Gets the auction image relation + # @return [Array] Auction images def self.get_relation(ah_id) imgs = self.get "*", "auction_id = ?", ah_id imgs.map! do |img| diff --git a/src/routes/auction.rb b/src/routes/auction.rb index a27b92c..05038b5 100644 --- a/src/routes/auction.rb +++ b/src/routes/auction.rb @@ -1,4 +1,4 @@ -# Auction stuff +# Auction index & searching page get "/auctions" do title = params[:title] and params[:title] != "" ? params[:title].strip : nil categories = params[:categories] @@ -11,10 +11,17 @@ get "/auctions" do serve :"auction/index", {auctions: auctions} end +# Auction creation form get "/auctions/new" do serve :"auction/new" end +# Create an auction +# @param [String] title +# @param [String] description +# @param [Float] init_price Initial price offering +# @param [Integer] delta_time Auction duration in hours +# @param [Array] images post "/auctions" do user_id = session[:userid] @@ -55,6 +62,8 @@ post "/auctions" do end end +# View auction +# @param [Integer] id get "/auctions/:id" do id = params[:id].to_i auction = Auction.find_by_id id @@ -66,6 +75,8 @@ get "/auctions/:id" do end end +# Edit auction form +# @param [Integer] id get "/auctions/:id/edit" do id = params[:id].to_i auction = Auction.find_by_id id @@ -81,6 +92,8 @@ get "/auctions/:id/edit" do end end +# Delete auction +# @param [Integer] id get "/auctions/:id/delete" do id = params[:id].to_i auction = Auction.find_by_id id @@ -99,6 +112,10 @@ get "/auctions/:id/delete" do end end +# Update auction credentials +# @param [Integer] id +# @param [String] title New title +# @param [String] description New description post "/auctions/:id/update" do id = params[:id].to_i auction = Auction.find_by_id id @@ -118,6 +135,10 @@ post "/auctions/:id/update" do end end +# Bid on an auction +# @param [Integer] id Auction id +# @param [Float] amount Must be greater than the current highest bid (min 1%) +# @param [String, nil] message post "/auctions/:id/bids" do id = params[:id].to_i auction = Auction.find_by_id id diff --git a/src/routes/user.rb b/src/routes/user.rb index ba4aa64..fa180f4 100644 --- a/src/routes/user.rb +++ b/src/routes/user.rb @@ -1,11 +1,15 @@ +# Login page for the user get "/login" do serve :"user/login", layout: :empty end +# Register page for the user get "/register" do serve :"user/register", layout: :empty end +# Profile for user +# @param [Integer] id User id get "/profile/:id" do id = params[:id].to_i userobj = User.find_by_id id @@ -17,6 +21,7 @@ get "/profile/:id" do end end +# Profile user for logged in user get "/profile" do if is_logged_in then redirect "/profile/#{session[:userid]}" @@ -25,8 +30,10 @@ get "/profile" do end end -# Reputation USER_REP_LIMIT ||= Hash.new(Hash.new(0)) +# Add or remove users reputation score +# @param [Integer] id User id +# @param [String] type Either "plus" or "minus" get "/user/rep/:id" do if !is_logged_in then session[:ret] = request.fullpath @@ -59,11 +66,16 @@ get "/user/rep/:id" do end end -# User stuff +# Logged in users settings get "/settings" do serve :"user/settings" end +# Register user +# @param [String] email +# @param [String] name +# @param [String] password +# @param [String] password_confirm post "/register" do email = params[:email] name = params[:name] @@ -81,6 +93,9 @@ post "/register" do end end +# Login user +# @param [String] email +# @param [String] password post "/login" do email = params[:email].strip password = params[:password].strip @@ -95,12 +110,17 @@ post "/login" do end end +# Logout current user get "/logout" do session.clear flash[:success] = "Successfully logged out!" redirect "/" end +# Update user credentials +# @param [Integer] id (Only applied if logged in user is admin), otherwise it defaults to current session user +# @param [String] displayname New user name +# @param [String] bio_text New bio text post "/user/update" do id = (get_current_user.admin? and params[:id]) ? params[:id].to_i : session[:userid]