require 'sinatra' require 'sinatra/activerecord' require 'securerandom' require_relative 'models' set :database, {adapter: "sqlite3", database: "db/todoizer.sqlite3"} set :sessions, true set :session_secret, ENV.fetch('SESSION_SECRET') { SecureRandom.hex(64) } set :server, 'puma' set :port, 4567 set :bind, '0.0.0.0' # Auto-migrate on startup for simplicity ActiveRecord::Schema.define do unless ActiveRecord::Base.connection.table_exists?(:users) create_table :users do |t| t.boolean :is_temporary, default: false t.string :username t.string :password_digest t.timestamps end end unless ActiveRecord::Base.connection.table_exists?(:todos) create_table :todos do |t| t.references :user, foreign_key: true t.string :content t.boolean :is_completed, default: false t.timestamps end end end helpers do def current_user @current_user ||= begin if session[:user_id] User.find_by(id: session[:user_id]) else nil end end end def ensure_user unless current_user temp_user = User.create!(is_temporary: true) session[:user_id] = temp_user.id @current_user = temp_user end end end before do ensure_user unless request.path_info == '/login' || request.path_info == '/signup' end get '/' do @todos = current_user.todos.order(created_at: :desc) erb :index end post '/todos' do if params[:content] && !params[:content].strip.empty? current_user.todos.create(content: params[:content].strip) end redirect '/' end post '/todos/:id/toggle' do todo = current_user.todos.find_by(id: params[:id]) todo.update(is_completed: !todo.is_completed) if todo redirect '/' end post '/todos/:id/delete' do todo = current_user.todos.find_by(id: params[:id]) todo.destroy if todo redirect '/' end get '/signup' do redirect '/' unless current_user&.is_temporary erb :signup end post '/signup' do redirect '/' unless current_user&.is_temporary username = params[:username].to_s.strip password = params[:password].to_s.strip if username.empty? || password.empty? @error = "Username and password are required." return erb :signup end if User.exists?(username: username) @error = "Username is already taken." return erb :signup end current_user.update!( is_temporary: false, username: username, password: password ) redirect '/' end get '/login' do redirect '/' if current_user && !current_user.is_temporary erb :login end post '/login' do user = User.find_by(username: params[:username], is_temporary: false) if user && user.authenticate(params[:password]) session[:user_id] = user.id redirect '/' else @error = "Invalid username or password" erb :login end end post '/logout' do session.clear redirect '/' end