diff --git a/app/controllers/api/v1/sessions_controller.rb b/app/controllers/api/v1/sessions_controller.rb index 50fc695..c376d67 100644 --- a/app/controllers/api/v1/sessions_controller.rb +++ b/app/controllers/api/v1/sessions_controller.rb @@ -1,26 +1,26 @@ -class Api::V1::SessionsController < ApplicationController - def create - @user = User.find_by(email: login_params[:email]) - - if @user&.valid_password?(login_params[:password]) - token = JsonWebToken.encode({ - user_id: @user.id, - jti: SecureRandom.uuid, - exp: Time.now.to_i + ENV["token_expire_time"].to_i - }) - response.headers['Authorization'] = "Bearer #{token}" - else - render_error :unauthorized, "Email or password is incorrect" - end - end - - def destroy - response.headers.delete('Authorization') - end - - private - - def login_params - params.permit User::LOGIN_PARAMS - end -end +class Api::V1::SessionsController < ApplicationController + def create + @user = User.find_by(email: login_params[:email]) + + if @user&.valid_password?(login_params[:password]) + token = JsonWebToken.encode({ + user_id: @user.id, + jti: SecureRandom.uuid, + exp: Time.now.to_i + ENV["token_expire_time"].to_i + }) + response.headers['Authorization'] = "Bearer #{token}" + else + render_error :unauthorized, "Email or password is incorrect" + end + end + + def destroy + response.headers.delete('Authorization') + end + + private + + def login_params + params.permit User::LOGIN_PARAMS + end +end diff --git a/app/controllers/api/v1/users_controller.rb b/app/controllers/api/v1/users_controller.rb index 48fdb81..3c3ece7 100644 --- a/app/controllers/api/v1/users_controller.rb +++ b/app/controllers/api/v1/users_controller.rb @@ -1,17 +1,17 @@ -class Api::V1::UsersController < ApplicationController - def create - @user = User.create(user_params) - - if @user.errors.blank? - render :create, status: :ok - else - render_error :bad_request, @user.errors.full_messages - end - end - - private - - def user_params - params.permit User::USER_PARAMS - end -end +class Api::V1::UsersController < ApplicationController + def create + @user = User.create(user_params) + + if @user.errors.blank? + render :create, status: :ok + else + render_error :bad_request, @user.errors.full_messages + end + end + + private + + def user_params + params.permit User::USER_PARAMS + end +end diff --git a/app/controllers/api/v1/wallets_controller.rb b/app/controllers/api/v1/wallets_controller.rb new file mode 100644 index 0000000..b2dd969 --- /dev/null +++ b/app/controllers/api/v1/wallets_controller.rb @@ -0,0 +1,77 @@ +class Api::V1::WalletsController < ApplicationController + before_action :authenticate_request! + before_action :find_wallet, only: [:show, :update, :destroy] + + def index + @wallets = [] + current_user.user_wallets.each do |user_wallet| + @wallets.push(user_wallet.wallet) + end + render :index, status: :ok + end + + def create + @wallet = Wallet.new(create_params) + + if @wallet.save + UserWallet.create( + user_id: current_user.id, + wallet_id: @wallet.id, + user_role: User::roles[:OWNER] + ) + + render :create, status: :created + else + render_error :bad_request, "Create wallet failed" + end + end + + def show + if accessible? + render :show, status: :ok + else + render_error :forbidden, "Not allow to access this wallet" + end + end + + def update + if owner? + @wallet.update(update_params) + render :update, status: :ok + else + render_error :forbidden, "Not allow to update this wallet" + end + end + + def destroy + if owner? + @wallet.destroy + else + render_error :forbidden, "Not allow to delete this wallet" + end + end + + private + + def create_params + params.permit(Wallet::CREATE_PARAMS) + end + + def update_params + params.permit(Wallet::UPDATE_PARAMS) + end + + def find_wallet + @wallet = Wallet.find_by(id: params[:id]) + + render_error :not_found, "Wallet ##{params[:id]} not found" unless @wallet + end + + def owner? + UserWallet.find_by(user_id: current_user.id, wallet_id: @wallet.id, user_role: User::roles[:OWNER]) + end + + def accessible? + UserWallet.find_by(user_id: current_user.id, wallet_id: @wallet.id) + end +end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index fa54cbc..2e0b1ea 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,8 +1,39 @@ class ApplicationController < ActionController::API + rescue_from Exception, with: :rescue_error + + attr_reader :current_user + protected + def authenticate_request! + unless user_id_in_token? + render_error :unauthorized, "Not authenticated" + return + end + @current_user = User.find(auth_token['user_id']) + rescue JWT::VerificationError, JWT::DecodeError + render_error :unauthorized, "Not authenticated" + end def render_error status, message @error_message = message render 'api/error', status: status end + + private + def http_token + @http_token = request.headers['Authorization'].split(' ').last if request.headers['Authorization'].present? + end + + def auth_token + @auth_token = JsonWebToken.decode(http_token) + end + + def user_id_in_token? + http_token && auth_token && auth_token[:user_id].to_i + end + + def rescue_error error + @error_message = error.message + render 'api/error', status: :bad_request + end end diff --git a/app/models/user.rb b/app/models/user.rb index 447de44..ebf9a48 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -2,6 +2,10 @@ class User < ApplicationRecord USER_PARAMS = %i(email password password_confirmation name).freeze LOGIN_PARAMS = %i(email password).freeze + enum roles: [:OWNER, :MANAGER, :OBSERVER] + + has_many :user_wallets, dependent: :destroy + # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable devise :database_authenticatable, :registerable, diff --git a/app/models/user_wallet.rb b/app/models/user_wallet.rb new file mode 100644 index 0000000..166ca40 --- /dev/null +++ b/app/models/user_wallet.rb @@ -0,0 +1,4 @@ +class UserWallet < ApplicationRecord + belongs_to :user + belongs_to :wallet +end diff --git a/app/models/wallet.rb b/app/models/wallet.rb new file mode 100644 index 0000000..acff356 --- /dev/null +++ b/app/models/wallet.rb @@ -0,0 +1,6 @@ +class Wallet < ApplicationRecord + CREATE_PARAMS = %i(name) + UPDATE_PARAMS = %i(name is_freezed) + + has_many :user_wallets, dependent: :destroy +end diff --git a/app/views/api/error.json.jbuilder b/app/views/api/error.json.jbuilder index bd5a7bc..49a5b4a 100644 --- a/app/views/api/error.json.jbuilder +++ b/app/views/api/error.json.jbuilder @@ -1,3 +1,3 @@ -json.data do - json.message @error_message || 'Some thing went wrong' -end +json.data do + json.message @error_message || 'Some thing went wrong' +end diff --git a/app/views/api/v1/sessions/create.json.jbuilder b/app/views/api/v1/sessions/create.json.jbuilder index 1c09018..0837c76 100644 --- a/app/views/api/v1/sessions/create.json.jbuilder +++ b/app/views/api/v1/sessions/create.json.jbuilder @@ -1,5 +1,5 @@ -json.data do - json.id @user.id - json.email @user.email - json.name @user.name -end +json.data do + json.id @user.id + json.email @user.email + json.name @user.name +end diff --git a/app/views/api/v1/sessions/destroy.json.jbuilder b/app/views/api/v1/sessions/destroy.json.jbuilder index 48035f0..bc6ec38 100644 --- a/app/views/api/v1/sessions/destroy.json.jbuilder +++ b/app/views/api/v1/sessions/destroy.json.jbuilder @@ -1,3 +1,3 @@ -json.data do - json.message "Logout successfully" -end +json.data do + json.message "Logout successfully" +end diff --git a/app/views/api/v1/users/create.json.jbuilder b/app/views/api/v1/users/create.json.jbuilder index 5ede574..8dc1985 100644 --- a/app/views/api/v1/users/create.json.jbuilder +++ b/app/views/api/v1/users/create.json.jbuilder @@ -1,3 +1,3 @@ -json.data do - json.user @user -end +json.data do + json.user @user +end diff --git a/app/views/api/v1/wallets/create.json.jbuilder b/app/views/api/v1/wallets/create.json.jbuilder new file mode 100644 index 0000000..9da88db --- /dev/null +++ b/app/views/api/v1/wallets/create.json.jbuilder @@ -0,0 +1,3 @@ +json.data do + json.wallet @wallet +end diff --git a/app/views/api/v1/wallets/destroy.json.jbuilder b/app/views/api/v1/wallets/destroy.json.jbuilder new file mode 100644 index 0000000..0254df5 --- /dev/null +++ b/app/views/api/v1/wallets/destroy.json.jbuilder @@ -0,0 +1,3 @@ +json.data do + json.message "Delete the wallet successfully" +end diff --git a/app/views/api/v1/wallets/index.json.jbuilder b/app/views/api/v1/wallets/index.json.jbuilder new file mode 100644 index 0000000..edb4f8a --- /dev/null +++ b/app/views/api/v1/wallets/index.json.jbuilder @@ -0,0 +1,3 @@ +json.data do + json.wallets @wallets +end diff --git a/app/views/api/v1/wallets/show.json.jbuilder b/app/views/api/v1/wallets/show.json.jbuilder new file mode 100644 index 0000000..9da88db --- /dev/null +++ b/app/views/api/v1/wallets/show.json.jbuilder @@ -0,0 +1,3 @@ +json.data do + json.wallet @wallet +end diff --git a/app/views/api/v1/wallets/update.json.jbuilder b/app/views/api/v1/wallets/update.json.jbuilder new file mode 100644 index 0000000..9da88db --- /dev/null +++ b/app/views/api/v1/wallets/update.json.jbuilder @@ -0,0 +1,3 @@ +json.data do + json.wallet @wallet +end diff --git a/config/routes.rb b/config/routes.rb index 74cf3a2..db3be55 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -7,6 +7,8 @@ post '/login', to: "sessions#create" post '/logout', to: "sessions#destroy" post '/signup', to: "users#create" + + resources :wallets, only: [:index, :create, :show, :update, :destroy] end end end diff --git a/db/migrate/20211110172151_create_wallets.rb b/db/migrate/20211110172151_create_wallets.rb new file mode 100644 index 0000000..ec7fb5d --- /dev/null +++ b/db/migrate/20211110172151_create_wallets.rb @@ -0,0 +1,11 @@ +class CreateWallets < ActiveRecord::Migration[6.1] + def change + create_table :wallets do |t| + t.string :name, null: false, default: "" + t.integer :total, null: false, default: 0 + t.boolean :is_freezed, null: false, default: false + + t.timestamps + end + end +end diff --git a/db/migrate/20211110173054_create_user_wallets.rb b/db/migrate/20211110173054_create_user_wallets.rb new file mode 100644 index 0000000..f371f50 --- /dev/null +++ b/db/migrate/20211110173054_create_user_wallets.rb @@ -0,0 +1,9 @@ +class CreateUserWallets < ActiveRecord::Migration[6.1] + def change + create_table :user_wallets do |t| + t.integer :user_role, null: false, default: 0 + + t.timestamps + end + end +end diff --git a/db/migrate/20211110173638_add_user_to_user_wallets.rb b/db/migrate/20211110173638_add_user_to_user_wallets.rb new file mode 100644 index 0000000..22bca9c --- /dev/null +++ b/db/migrate/20211110173638_add_user_to_user_wallets.rb @@ -0,0 +1,5 @@ +class AddUserToUserWallets < ActiveRecord::Migration[6.1] + def change + add_reference :user_wallets, :user, foreign_key: true + end +end diff --git a/db/migrate/20211110173738_add_wallet_to_user_wallets.rb b/db/migrate/20211110173738_add_wallet_to_user_wallets.rb new file mode 100644 index 0000000..574f37f --- /dev/null +++ b/db/migrate/20211110173738_add_wallet_to_user_wallets.rb @@ -0,0 +1,5 @@ +class AddWalletToUserWallets < ActiveRecord::Migration[6.1] + def change + add_reference :user_wallets, :wallet, foreign_key: true + end +end diff --git a/db/schema.rb b/db/schema.rb index 40974e2..1cd4e9f 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,17 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2021_11_08_072758) do +ActiveRecord::Schema.define(version: 2021_11_10_173738) do + + create_table "user_wallets", charset: "utf8mb4", force: :cascade do |t| + t.integer "user_role", default: 0, null: false + t.datetime "created_at", precision: 6, null: false + t.datetime "updated_at", precision: 6, null: false + t.bigint "user_id" + t.bigint "wallet_id" + t.index ["user_id"], name: "index_user_wallets_on_user_id" + t.index ["wallet_id"], name: "index_user_wallets_on_wallet_id" + end create_table "users", charset: "utf8mb4", force: :cascade do |t| t.string "email", default: "", null: false @@ -29,4 +39,14 @@ t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true end + create_table "wallets", charset: "utf8mb4", force: :cascade do |t| + t.string "name", default: "", null: false + t.integer "total", default: 0, null: false + t.boolean "is_freezed", default: false, null: false + t.datetime "created_at", precision: 6, null: false + t.datetime "updated_at", precision: 6, null: false + end + + add_foreign_key "user_wallets", "users" + add_foreign_key "user_wallets", "wallets" end diff --git a/test/fixtures/user_wallets.yml b/test/fixtures/user_wallets.yml new file mode 100644 index 0000000..5181636 --- /dev/null +++ b/test/fixtures/user_wallets.yml @@ -0,0 +1,11 @@ +# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +# This model initially had no columns defined. If you add columns to the +# model remove the '{}' from the fixture names and add the columns immediately +# below each fixture, per the syntax in the comments below +# +one: {} +# column: value +# +two: {} +# column: value diff --git a/test/fixtures/wallets.yml b/test/fixtures/wallets.yml new file mode 100644 index 0000000..5181636 --- /dev/null +++ b/test/fixtures/wallets.yml @@ -0,0 +1,11 @@ +# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +# This model initially had no columns defined. If you add columns to the +# model remove the '{}' from the fixture names and add the columns immediately +# below each fixture, per the syntax in the comments below +# +one: {} +# column: value +# +two: {} +# column: value diff --git a/test/models/user_wallet_test.rb b/test/models/user_wallet_test.rb new file mode 100644 index 0000000..c84d94e --- /dev/null +++ b/test/models/user_wallet_test.rb @@ -0,0 +1,7 @@ +require "test_helper" + +class UserWalletTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/test/models/wallet_test.rb b/test/models/wallet_test.rb new file mode 100644 index 0000000..fa46e40 --- /dev/null +++ b/test/models/wallet_test.rb @@ -0,0 +1,7 @@ +require "test_helper" + +class WalletTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end