`, ``, and ``.
23 | $font-family-monospace: Menlo, Monaco, Consolas, "Courier New", monospace;
24 | $font-family-base: $font-family-sans-serif;
25 |
26 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/frontend/auth.scss:
--------------------------------------------------------------------------------
1 | @import "_vars";
2 |
3 | .authWrapper{
4 | width: 100%;
5 | max-width: 420px;
6 | margin: 100px auto;
7 | padding: 20px 30px;
8 | border-radius: 6px;
9 | background-color: white;
10 | text-align: center;
11 | border: 1px solid $color--border;
12 | box-shadow: 1px 1px 3px rgba(0,0,0,.1);
13 | }
--------------------------------------------------------------------------------
/app/assets/stylesheets/frontend/main.scss:
--------------------------------------------------------------------------------
1 | @import "_vars";
2 |
3 | /* TOOLS ======================================================== */
4 | /* External tools, mixins, functions ============================ */
5 |
6 |
7 | /* GENERIC ====================================================== */
8 | /* Project-agnostic, normalizers, box-sizing, etc. ============== */
9 |
10 | * { box-sizing: border-box; }
11 |
12 | /* ELEMENTS ===================================================== */
13 | /* Basic styling for any HTML-tag without classes =============== */
14 |
15 | body {
16 | font-size: 17px;
17 | font-family: $font-family-base;
18 | -webkit-font-smoothing: antialiased;
19 | font-weight: 400;
20 | overflow-x: hidden;
21 | width: 100%;
22 | margin-left: auto;
23 | margin-right: auto;
24 | background-color: $gray-lighter;
25 | }
26 |
27 | a{
28 | color: $brand-primary;
29 | text-decoration: none;
30 |
31 | &:hover{
32 | text-decoration: underline;
33 | }
34 | }
35 |
36 | code{
37 | font-size: 0.95rem;
38 | font-weight: 300;
39 | padding: 3px;
40 | margin: 0 3px;
41 | border-radius: 4px;
42 | line-height: 1;
43 | background-color: $gray-lighter;
44 | }
45 |
46 | input{
47 | display: inline-block;
48 | margin: 5px 0;
49 | border-radius: 6px;
50 | width: 100%;
51 | padding: 8px 16px;
52 | background-color: rgb(255,255,255);
53 | border: 1px solid $color--border;
54 | }
55 |
56 | input[type=submit],
57 | .btn {
58 | margin: 20px 0;
59 | display: inline-block;
60 | box-sizing: border-box;
61 | padding: 12px 24px;
62 | -moz-appearance: none;
63 | cursor: pointer;
64 | border-radius: 6px;
65 | background-color: $brand-primary;
66 | color: #ffffff;
67 | text-transform: uppercase;
68 | font-weight: 700;
69 | width: 100%;
70 | border: medium none;
71 | line-height: 18px;
72 | -moz-box-align: center;
73 | align-items: center;
74 | margin-left: auto;
75 | margin-right: auto;
76 | }
77 |
78 | h1,h2,h3,h4,h5,h6{
79 | font-family: $font-family-serif;
80 | color: $gray;
81 | }
82 |
83 | .field{
84 | text-align: left;
85 | }
86 |
87 | .logo-wrapper{
88 | background-color: white;
89 | display: inline-block;
90 | padding: 15px 10px 10px 10px;
91 | }
92 |
93 | .flash{
94 | padding: 10px;
95 | text-align: center;
96 | background: $brand-info;
97 | margin: 10px 0;
98 | color: white;
99 | border-radius: 3px;
100 | font-weight: 700;
101 |
102 | &--error{
103 | background: $brand-danger;
104 | }
105 |
106 | &--alert{
107 | background: $brand-warning;
108 | }
109 | }
--------------------------------------------------------------------------------
/app/assets/stylesheets/frontend/typo.scss:
--------------------------------------------------------------------------------
1 | @import "_vars";
2 |
3 | h1,h2,h3,h4,h5,h6{
4 | font-family: $font-family-serif;
5 | font-weight: 600;
6 | line-height: 1.2;
7 | }
--------------------------------------------------------------------------------
/app/assets/stylesheets/frontend/util.scss:
--------------------------------------------------------------------------------
1 | .clearfix{
2 | float: none;
3 | clear: both;
4 | display: block;
5 | }
--------------------------------------------------------------------------------
/app/assets/stylesheets/rails_admin/custom/theming.scss:
--------------------------------------------------------------------------------
1 |
2 | @import url('https://fonts.googleapis.com/css?family=Roboto:400,400i,700|Roboto+Slab:400,700&display=swap&subset=latin-ext');
3 |
4 | body.rails_admin{
5 |
6 | h1,h2,h3,h4,h5,h6{
7 | font-family: $font-family-serif;
8 | color: $gray-dark;
9 | }
10 |
11 | .navbar{
12 | border-bottom: 3px solid white;
13 | box-shadow: 1px 2px 5px rgba(0,0,0,.1);
14 | max-height: 53px;
15 | overflow: visible;
16 |
17 | }
18 |
19 | .nav {
20 | .icon, .fa{
21 | margin-right: 12px;
22 | }
23 | }
24 |
25 | .logo-wrapper{
26 | display: inline-block;
27 | padding: 15px 10px 10px 10px;
28 | }
29 |
30 | .fieldset .label.label-info{
31 | border-bottom-left-radius: 0;
32 | border-bottom-right-radius: 0;
33 | }
34 |
35 | .fieldset .well{
36 | border-top-left-radius: 0;
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/app/controllers/application_controller.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Base controlller for application
4 | class ApplicationController < ActionController::Base
5 | include HttpAuth
6 | include LocaleWrapper
7 |
8 | rescue_from CanCan::AccessDenied do |exception|
9 | respond_to do |format|
10 | format.js { render 'errors/unauthorized', status: 403 }
11 | format.html { redirect_to '/', alert: exception.message }
12 | format.json { render json: { errors: { permission: [exception.message] } }, status: 403 }
13 | end
14 | end
15 |
16 | protected
17 |
18 | def current_superadmin
19 | return nil if current_user.nil?
20 | return nil unless current_user.superadmin?
21 |
22 | current_user
23 | end
24 |
25 | private
26 |
27 | def after_sign_in_path_for(resource)
28 | if resource.superadmin? || resource.admin?
29 | rails_admin_url
30 | else
31 | root_url
32 | end
33 | end
34 | end
35 |
--------------------------------------------------------------------------------
/app/controllers/auth/confirmations_controller.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Auth
4 | # Custom confirmations controller
5 | class ConfirmationsController < Devise::ConfirmationsController
6 | # GET /resource/confirmation?confirmation_token=abcdef
7 | def show
8 | self.resource = resource_class.confirm_by_token(params[:confirmation_token])
9 | yield resource if block_given?
10 |
11 | if resource.errors.empty?
12 | respond_with_navigational(resource) { redirect_to after_confirmation_path_for(resource_name, resource) }
13 | else
14 | redirect_to "http://#{ENV['CLIENT_URL']}?error=#{I18n.t('errors.messages.already_confirmed')}"
15 | end
16 | end
17 |
18 | private
19 |
20 | # redirect user to front end app after confirming the email adress.
21 | def after_confirmation_path_for(_resource_name, _resource)
22 | "http://#{ENV['CLIENT_URL']}?notice=#{I18n.t('devise.confirmations.confirmed')}"
23 | end
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/app/controllers/auth/invitations_controller.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Auth
4 | # Custom passwords controller
5 | class InvitationsController < Devise::InvitationsController
6 | # GET /resource/invitation/accept?invitation_token=abcdef
7 | # redirect user to front end to finish invitation
8 | def edit
9 | redirect_to "http://#{ENV['CLIENT_URL']}/users/invitation/accept?invitation_token=#{params[:invitation_token]}"
10 | end
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/app/controllers/auth/passwords_controller.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # rubocop:disable Layout/LineLength
4 | module Auth
5 | # Custom passwords controller
6 | class PasswordsController < Devise::PasswordsController
7 | # GET /resource/password/edit?reset_password_token=abcdef
8 | # redirect user to front end to reset the passwords there
9 | def edit
10 | redirect_to "http://#{ENV['CLIENT_URL']}/users/password/edit?reset_password_token=#{params[:reset_password_token]}"
11 | end
12 | end
13 | end
14 | # rubocop:enable Layout/LineLength
15 |
--------------------------------------------------------------------------------
/app/controllers/concerns/http_auth.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Injects http authentication to a controller.
4 | module HttpAuth
5 | extend ActiveSupport::Concern
6 |
7 | included do
8 | before_action :http_authenticate
9 | end
10 |
11 | protected
12 |
13 | def http_authenticate
14 | return true unless ENV['IS_HTTP_AUTH_PROTECTED'] == 'true'
15 |
16 | authenticate_or_request_with_http_basic do |username, password|
17 | username == ENV['HTTP_AUTH_USER'] && password == ENV['HTTP_AUTH_PASSWORD']
18 | end
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/app/controllers/concerns/locale_wrapper.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Handles setting of locale in your controller.
4 | module LocaleWrapper
5 | extend ActiveSupport::Concern
6 |
7 | included do
8 | around_action :switch_locale
9 | end
10 |
11 | protected
12 |
13 | def switch_locale(&action)
14 | locale = params[:locale] || locale_from_http_accept_lang || I18n.default_locale
15 | I18n.with_locale(locale, &action)
16 | end
17 |
18 | def locale_from_http_accept_lang
19 | return nil if !request || !request.env['HTTP_ACCEPT_LANGUAGE']
20 |
21 | locale = request.env['HTTP_ACCEPT_LANGUAGE'].scan(/^[a-z]{2}/).first
22 | return nil unless locale
23 |
24 | # only if match
25 | I18n.available_locales.map(&:to_s).include?(locale) ? locale : nil
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/app/controllers/graphql_controller.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # GraphQL API entry point
4 | class GraphqlController < ApplicationController
5 | include Graphql::AuthHelper
6 |
7 | # disable forgery protection for API
8 | skip_before_action :verify_authenticity_token
9 |
10 | # Executes the graphql request
11 | def execute
12 | query, variables, operation_name = read_query_params()
13 | result = GraphqlSchema.execute(
14 | query,
15 | variables: variables,
16 | context: context,
17 | operation_name: operation_name
18 | )
19 | render json: result
20 | rescue StandardError => e
21 | handle_error e
22 | end
23 |
24 | private
25 |
26 | def read_query_params
27 | [
28 | params[:query],
29 | ensure_hash(params[:variables]),
30 | params[:operationName]
31 | ]
32 | end
33 |
34 | # Handle form data, JSON body, or a blank value
35 | def ensure_hash(ambiguous_param)
36 | case ambiguous_param
37 | when String
38 | ambiguous_param.present? ? ensure_hash(JSON.parse(ambiguous_param)) : {}
39 | when Hash, ActionController::Parameters
40 | ambiguous_param
41 | when nil
42 | {}
43 | else
44 | raise ArgumentError, "Unexpected parameter: #{ambiguous_param}"
45 | end
46 | end
47 |
48 | def handle_error(err)
49 | logger.error err.message
50 | logger.error err.backtrace.join("\n")
51 |
52 | render json: {
53 | errors: [
54 | { message: err.message }
55 | ],
56 | data: {}
57 | }, status: 500
58 | end
59 | end
60 |
--------------------------------------------------------------------------------
/app/controllers/pages_controller.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # A controller to handle static pages served from backend
4 | class PagesController < ApplicationController
5 | def blank
6 | head 200, content_type: 'text/html'
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/app/graphql/graphql_schema.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Entry point for graphql schema
4 | class GraphqlSchema < GraphQL::Schema
5 | disable_introspection_entry_points if Rails.env.production?
6 | query(Types::QueryType)
7 | mutation(Types::MutationType)
8 | end
9 |
--------------------------------------------------------------------------------
/app/graphql/mutations/base_mutation.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Mutations
4 | # Base mutation class for app
5 | class BaseMutation < GraphQL::Schema::Mutation
6 | def current_ability
7 | Ability.new(context[:current_user])
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/app/graphql/mutations/companies/update_company.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Mutations
4 | module Companies
5 | # Updates an existing company.
6 | class UpdateCompany < Mutations::BaseMutation
7 | description 'Updates an existing company.'
8 | argument :id, ID, required: true
9 | argument :attributes, Types::Companies::CompanyInputType, required: true
10 | payload_type Types::Companies::CompanyType
11 |
12 | def resolve(id:, attributes:)
13 | # find company
14 | company = ::Companies::Company.accessible_by(current_ability).find_by(id: id)
15 | if company.nil?
16 | raise ActiveRecord::RecordNotFound,
17 | I18n.t('errors.messages.resource_not_found', resource: ::Companies::Company.model_name.human)
18 | end
19 |
20 | company.attributes = attributes.to_h
21 | current_ability.authorize! :update, company
22 | return company if company.save!
23 | end
24 | end
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/app/graphql/mutations/users/accept_invite.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Mutations
4 | module Users
5 | # Accepts an invitation for a user
6 | class AcceptInvite < Mutations::BaseMutation
7 | description 'Accepts an invitation for a user'
8 | argument :invitation_token, String, required: true
9 | argument :attributes, Types::Users::UserInputType, required: true
10 | payload_type Boolean
11 |
12 | def resolve(invitation_token:, attributes:)
13 | user = User.accept_invitation!(attributes.to_h.merge(invitation_token: invitation_token))
14 | raise ActiveRecord::RecordInvalid, user unless user.errors.empty?
15 |
16 | true
17 | end
18 | end
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/app/graphql/mutations/users/delete_user.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Mutations
4 | module Users
5 | # Deletes an user as an admin.
6 | class DeleteUser < Mutations::BaseMutation
7 | description 'Deletes an user as an admin.'
8 | argument :id, ID, required: true
9 | payload_type Boolean
10 |
11 | def resolve(id:)
12 | user = ::User.accessible_by(current_ability).find_by(id: id)
13 | if user.nil?
14 | raise ActiveRecord::RecordNotFound,
15 | I18n.t('errors.messages.resource_not_found', resource: ::User.model_name.human)
16 | end
17 |
18 | current_ability.authorize! :destroy, user
19 | return true if user.destroy!
20 |
21 | false
22 | end
23 | end
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/app/graphql/mutations/users/invite_user.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Mutations
4 | module Users
5 | # Invites an user to your account.
6 | class InviteUser < Mutations::BaseMutation
7 | description 'Invites an user to your account.'
8 | argument :attributes, Types::Users::UserInputType, required: true
9 | payload_type Types::Users::UserType
10 |
11 | def resolve(attributes:)
12 | # create a dummy user object to check ability against create
13 | user = ::User.new(attributes.to_h.merge(company_id: context[:current_user].company_id))
14 | current_ability.authorize! :create, user
15 |
16 | user = User.invite!(user.attributes, context[:current_user])
17 | raise ActiveRecord::RecordInvalid, user unless user.errors.empty?
18 |
19 | user
20 | end
21 | end
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/app/graphql/mutations/users/update_user.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Mutations
4 | module Users
5 | # Updates an existing user as an admin.
6 | class UpdateUser < Mutations::BaseMutation
7 | description 'Updates an existing user as an admin.'
8 | argument :id, ID, required: true
9 | argument :attributes, Types::Users::UserInputType, required: true
10 | payload_type Types::Users::UserType
11 |
12 | def resolve(id:, attributes:)
13 | user = ::User.accessible_by(current_ability).find_by(id: id)
14 | if user.nil?
15 | raise ActiveRecord::RecordNotFound,
16 | I18n.t('errors.messages.resource_not_found', resource: ::User.model_name.human)
17 | end
18 |
19 | user.attributes = attributes.to_h
20 | current_ability.authorize! :update, user
21 | return user if user.save!
22 | end
23 | end
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/app/graphql/mutations/users/update_user_role.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Mutations
4 | module Users
5 | # Updates the role for an user as an admin.
6 | class UpdateUserRole < Mutations::BaseMutation
7 | description 'Updates the role for an user as an admin.'
8 | argument :id, ID, required: true
9 | argument :role, String, required: true, description: '"user" or "admin"'
10 | payload_type Boolean
11 |
12 | def resolve(id:, role:)
13 | user = ::User.accessible_by(current_ability).find_by(id: id)
14 | if user.nil?
15 | raise ActiveRecord::RecordNotFound,
16 | I18n.t('errors.messages.resource_not_found', resource: ::User.model_name.human)
17 | end
18 |
19 | if %w[admin user].include?(role)
20 | user.role = role
21 | current_ability.authorize! :update, user
22 | user.save!
23 | return true
24 | end
25 |
26 | false
27 | end
28 | end
29 | end
30 | end
31 |
--------------------------------------------------------------------------------
/app/graphql/resolvers/base_resolver.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Resolvers
4 | # Base resolver class include everything you need for sorting and filtering entities
5 | class BaseResolver < GraphQL::Schema::Resolver
6 | # override in your resolver to allow order by attributes
7 | def allowed_filter_attributes
8 | raise 'Return an array with your allowed filter attributes.'
9 | end
10 |
11 | # apply_filter recursively loops through "OR" branches
12 | def apply_filter(scope, value)
13 | branches = normalize_filters(value).reduce { |a, b| a.or(b) }
14 | scope.merge branches
15 | end
16 |
17 | def normalize_filters(value, branches = [])
18 | scope = resources
19 | allowed_filter_attributes.each do |filter_attr|
20 | if value[filter_attr.to_sym].present?
21 | scope = scope.where("#{filter_attr} LIKE ?", "%#{value[filter_attr.to_sym]}%")
22 | end
23 | end
24 | branches << scope
25 | value[:OR].reduce(branches) { |s, v| normalize_filters(v, s) } if value[:OR].present?
26 | branches
27 | end
28 |
29 | # override in your resolver to allow order by attributes
30 | def allowed_order_attributes
31 | raise 'Return an array with your allowed order attributes.'
32 | end
33 |
34 | # apply order_by
35 | def apply_order_by(scope, value)
36 | direction = 'asc'
37 | if value[:attribute].present? &&
38 | allowed_order_attributes.include?(value[:attribute])
39 | direction = value[:direction] if value[:direction].present? && %w[asc desc].include?(value[:direction].downcase)
40 | scope = scope.order("#{value[:attribute]} #{direction}")
41 | end
42 | scope
43 | end
44 |
45 | def current_ability
46 | Ability.new(context[:current_user])
47 | end
48 | end
49 | end
50 |
--------------------------------------------------------------------------------
/app/graphql/resolvers/companies/company.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Resolvers
4 | module Companies
5 | # Resolver to return a user
6 | class Company < ::Resolvers::BaseResolver
7 | type Types::Companies::CompanyType, null: true
8 | description 'Returns the company for the user.'
9 |
10 | def resolve
11 | context[:current_user] ? context[:current_user].company : nil
12 | end
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/app/graphql/resolvers/users/me.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Resolvers
4 | module Users
5 | # Get current user object
6 | class Me < Resolvers::BaseResolver
7 | type Types::Users::UserType, null: true
8 | description 'Returns the current user'
9 |
10 | def resolve
11 | context[:current_user]
12 | end
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/app/graphql/resolvers/users/user.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Resolvers
4 | module Users
5 | # Resolver to return a user
6 | class User < Resolvers::BaseResolver
7 | type Types::Users::UserType, null: true
8 | description 'Returns the user for a requested id'
9 |
10 | argument :id, ID, required: true
11 |
12 | def resolve(id:)
13 | ::User.accessible_by(current_ability).find_by(id: id)
14 | end
15 | end
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/app/graphql/resolvers/users/users.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Resolvers
4 | module Users
5 | # Resolver to return a user
6 | class Users < Resolvers::BaseResolver
7 | include ::SearchObject.module(:graphql)
8 |
9 | type Types::Users::UserType.connection_type, null: false
10 | description 'Returns all user for the current user company'
11 | scope { resources }
12 |
13 | def resources
14 | ::User.accessible_by(current_ability).includes(:company)
15 | end
16 |
17 | option :order_by, type: Types::ItemOrderType, with: :apply_order_by
18 | def allowed_order_attributes
19 | %w[email first_name last_name created_at updated_at]
20 | end
21 |
22 | # inline input type definition for the advanced filter
23 | class UserFilterType < ::Types::BaseInputObject
24 | argument :OR, [self], required: false
25 | argument :email, String, required: false
26 | argument :first_name, String, required: false
27 | argument :last_name, String, required: false
28 | end
29 | option :filter, type: UserFilterType, with: :apply_filter
30 | def allowed_filter_attributes
31 | %w[email first_name last_name]
32 | end
33 | end
34 | end
35 | end
36 |
--------------------------------------------------------------------------------
/app/graphql/types/base_input_object.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Types
4 | # Base class for all graphql input objects
5 | class BaseInputObject < GraphQL::Schema::InputObject
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/app/graphql/types/base_model.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Types
4 | # Base class for all graphql model objects
5 | class BaseModel < GraphQL::Schema::Object
6 | field :id, ID, null: false
7 | field :created_at, GraphQL::Types::ISO8601DateTime, null: false
8 | field :updated_at, GraphQL::Types::ISO8601DateTime, null: false
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/app/graphql/types/base_object.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Types
4 | # Base class for all graphql objects
5 | class BaseObject < GraphQL::Schema::Object
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/app/graphql/types/companies/company_input_type.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Types
4 | module Companies
5 | # Attributes to update a company.
6 | class CompanyInputType < Types::BaseInputObject
7 | description 'Attributes to update a company.'
8 | argument :name, String, 'Name of company', required: true
9 | end
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/app/graphql/types/companies/company_type.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Types
4 | module Companies
5 | # GraphQL type for a company
6 | class CompanyType < Types::BaseModel
7 | field :name, String, null: false
8 | field :slug, String, null: true
9 | # field :users, [::Types::UserType], null: true
10 | end
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/app/graphql/types/item_order_type.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Types
4 | # Base type for sort objects
5 | class ItemOrderType < BaseInputObject
6 | argument :attribute, String, required: true, description: 'The attribute you want to order by.'
7 | argument :direction, String, required: true, description: 'Set a direction with "asc" or "desc".'
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/app/graphql/types/mutation_type.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Types
4 | # Loads mutations into schema
5 | # Include ither mutations here
6 | class MutationType < Types::BaseObject
7 | implements ::Types::GraphqlAuth
8 |
9 | # Mutations
10 | field :update_user, mutation: Mutations::Users::UpdateUser
11 | field :update_user_role, mutation: Mutations::Users::UpdateUserRole
12 | field :delete_user, mutation: Mutations::Users::DeleteUser
13 | field :invite_user, mutation: Mutations::Users::InviteUser
14 | field :accept_invite, mutation: Mutations::Users::AcceptInvite
15 |
16 | # Company
17 | field :update_company, mutation: Mutations::Companies::UpdateCompany
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/app/graphql/types/query_type.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Types
4 | # Loads queries into schema
5 | # include other queries and resolvers here
6 | class QueryType < BaseObject
7 | field :me, resolver: Resolvers::Users::Me
8 |
9 | field :users, resolver: Resolvers::Users::Users
10 | field :user, resolver: Resolvers::Users::User
11 |
12 | field :company, resolver: Resolvers::Companies::Company
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/app/graphql/types/users/user_input_type.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Types
4 | module Users
5 | # Input type for user
6 | class UserInputType < Types::BaseInputObject
7 | description 'Attributes to create a user.'
8 | argument :email, String, 'Email of user', required: true
9 | argument :first_name, String, 'Firstname of user', required: true
10 | argument :last_name, String, 'Lastname of user', required: true
11 | argument :password, String, 'Password of user', required: false
12 | argument :password_confirmation, String, 'Password confirmation', required: false
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/app/graphql/types/users/user_type.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Types
4 | module Users
5 | # GraphQL type for a user
6 | class UserType < Types::BaseModel
7 | field :name, String, null: false
8 | field :first_name, String, null: false
9 | field :last_name, String, null: false
10 | field :email, String, null: true
11 | field :role, String, null: false
12 | field :company, Types::Companies::CompanyType, null: false
13 |
14 | field :is_confirmed, Boolean, null: false
15 | def is_confirmed
16 | object.confirmed?
17 | end
18 |
19 | field :is_locked, Boolean, null: false
20 | def is_locked
21 | object.access_locked?
22 | end
23 | end
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/app/jobs/application_job.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Base class for all background jobs
4 | # :nocov:
5 | class ApplicationJob < ActiveJob::Base
6 | end
7 | # :nocov:
8 |
--------------------------------------------------------------------------------
/app/mailers/application_mailer.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Base class for all application mailer
4 | # :nocov:
5 | class ApplicationMailer < ActionMailer::Base
6 | default from: ENV['DEVISE_MAILER_FROM']
7 | layout 'mailer'
8 | end
9 | # :nocov:
10 |
--------------------------------------------------------------------------------
/app/models/ability.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Defines abilities for user
4 | class Ability
5 | include CanCan::Ability
6 |
7 | def initialize(user)
8 | user ||= User.new # guest user (not logged in)
9 |
10 | alias_action :create, :read, :update, :destroy, to: :crud
11 |
12 | return if user.new_record?
13 |
14 | if user.superadmin?
15 | can :access, :rails_admin # grant access to rails_admin
16 | can :manage, :all # admins can manage all objects
17 | elsif user.admin?
18 | can :crud, User, company_id: user.company_id
19 | can :update, Companies::Company, id: user.company_id
20 | end
21 | can :read, Companies::Company, id: user.company_id
22 | can :read, User, company_id: user.company_id
23 |
24 | # See the wiki for details:
25 | # https://github.com/CanCanCommunity/cancancan/wiki/Defining-Abilities
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/app/models/application_record.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Base class for all application records
4 | class ApplicationRecord < ActiveRecord::Base
5 | self.abstract_class = true
6 | end
7 |
--------------------------------------------------------------------------------
/app/models/companies/company.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # == Schema Information
4 | #
5 | # Table name: companies
6 | #
7 | # id :uuid not null, primary key
8 | # name :string
9 | # slug :string
10 | # users_count :integer
11 | # created_at :datetime not null
12 | # updated_at :datetime not null
13 | #
14 | # Indexes
15 | #
16 | # index_companies_on_slug (slug) UNIQUE
17 | #
18 | module Companies
19 | # A company to scope a bunch of users
20 | class Company < ApplicationRecord
21 | extend FriendlyId
22 |
23 | self.table_name = 'companies'
24 |
25 | # - EXTENSIONS
26 | friendly_id :name, use: :slugged
27 |
28 | # - VALIDATIONS
29 | validates :name, presence: true
30 | validates :name, length: { maximum: 255 }
31 | validates :slug, length: { maximum: 255 }
32 |
33 | # - RELATIONS
34 | has_many :users, dependent: :destroy
35 |
36 | # override friendly id checker for categories
37 | def should_generate_new_friendly_id?
38 | (slug.nil? || slug.blank?) || (name_changed? && !slug_changed?)
39 | end
40 |
41 | # :nocov:
42 | rails_admin do
43 | weight 10
44 | navigation_icon 'fa fa-building'
45 |
46 | list do
47 | field :id
48 | field :name
49 | field :slug
50 | field :users_count
51 | end
52 |
53 | edit do
54 | field :name
55 | field :slug
56 | end
57 |
58 | show do
59 | field :id
60 | field :name
61 | field :slug
62 | field :users_count
63 | end
64 | end
65 |
66 | # :nocov:
67 | end
68 | end
69 |
--------------------------------------------------------------------------------
/app/models/concerns/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zauberware/rails-devise-graphql/f3e71d39e9e5bb7d282af4546c65ccead6c67fb0/app/models/concerns/.keep
--------------------------------------------------------------------------------
/app/models/user.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # == Schema Information
4 | #
5 | # Table name: users
6 | #
7 | # id :uuid not null, primary key
8 | # confirmation_sent_at :datetime
9 | # confirmation_token :string
10 | # confirmed_at :datetime
11 | # current_sign_in_at :datetime
12 | # current_sign_in_ip :inet
13 | # email :string default(""), not null
14 | # encrypted_password :string default(""), not null
15 | # failed_attempts :integer default(0), not null
16 | # first_name :string default(""), not null
17 | # invitation_accepted_at :datetime
18 | # invitation_created_at :datetime
19 | # invitation_limit :integer
20 | # invitation_sent_at :datetime
21 | # invitation_token :string
22 | # invitations_count :integer default(0)
23 | # invited_by_type :string
24 | # last_name :string default(""), not null
25 | # last_seen_at :datetime
26 | # last_sign_in_at :datetime
27 | # last_sign_in_ip :inet
28 | # locked_at :datetime
29 | # refresh_token :string
30 | # remember_created_at :datetime
31 | # reset_password_sent_at :datetime
32 | # reset_password_token :string
33 | # role :integer default("user"), not null
34 | # sign_in_count :integer default(0), not null
35 | # unconfirmed_email :string
36 | # unlock_token :string
37 | # created_at :datetime not null
38 | # updated_at :datetime not null
39 | # company_id :uuid
40 | # invited_by_id :bigint
41 | #
42 | # Indexes
43 | #
44 | # index_users_on_confirmation_token (confirmation_token) UNIQUE
45 | # index_users_on_email (email) UNIQUE
46 | # index_users_on_invitation_token (invitation_token) UNIQUE
47 | # index_users_on_invitations_count (invitations_count)
48 | # index_users_on_invited_by_id (invited_by_id)
49 | # index_users_on_invited_by_type_and_invited_by_id (invited_by_type,invited_by_id)
50 | # index_users_on_refresh_token (refresh_token) UNIQUE
51 | # index_users_on_reset_password_token (reset_password_token) UNIQUE
52 | # index_users_on_unlock_token (unlock_token) UNIQUE
53 | #
54 | class User < ApplicationRecord
55 | # Include default devise modules. Others available are:
56 | # :timeoutable, :trackable and :omniauthable
57 | devise :invitable, :database_authenticatable,
58 | :registerable,
59 | :recoverable,
60 | :confirmable,
61 | :devise,
62 | :validatable,
63 | :lockable,
64 | :trackable,
65 | :invitable
66 |
67 | # add new roles to the end
68 | enum role: { user: 0, admin: 1, superadmin: 2 }
69 |
70 | # - VALIDATIONS
71 | validates :email, presence: true
72 | validates :email, length: { maximum: 255 }
73 | validates :email, format: { with: Regex::Email::VALIDATE }
74 | validates :first_name, length: { maximum: 255 }
75 | validates :last_name, length: { maximum: 255 }
76 |
77 | # - RELATIONS
78 | belongs_to :company, counter_cache: true, class_name: 'Companies::Company'
79 |
80 | # - CALLBACKS
81 | after_initialize :setup_new_user, if: :new_record?
82 | before_validation :setup_company
83 |
84 | # return first and lastname
85 | def name
86 | [first_name, last_name].join(' ').strip
87 | end
88 |
89 | def status_color
90 | return 'warning' if role == 'admin'
91 | return 'danger' if role == 'superadmin'
92 |
93 | 'primary'
94 | end
95 |
96 | private
97 |
98 | def setup_company
99 | return unless company.nil?
100 |
101 | self.company = Companies::Company.create!(name: 'My company')
102 | self.role = :admin # make this user the admin
103 | end
104 |
105 | def setup_new_user
106 | self.role ||= :user
107 | end
108 |
109 | # :nocov:
110 | # rubocop:disable Metrics/BlockLength
111 | rails_admin do
112 | weight 10
113 | navigation_icon 'fa fa-user-circle'
114 |
115 | configure :role do
116 | pretty_value do # used in list view columns and show views, defaults to formatted_value for non-association fields
117 | bindings[:view].tag.span(
118 | User.human_attribute_name(value), class: "label label-#{bindings[:object].status_color}"
119 | )
120 | end
121 | export_value do
122 | User.human_attribute_name(value)
123 | end
124 | end
125 |
126 | configure :email do
127 | pretty_value do
128 | bindings[:view].link_to(value, "mailto:#{value}")
129 | end
130 | export_value do
131 | value
132 | end
133 | end
134 |
135 | list do
136 | field :id
137 | field :first_name
138 | field :last_name
139 | field :email
140 | field :role
141 | field :last_sign_in_at
142 | field :company
143 | end
144 |
145 | edit do
146 | field :first_name
147 | field :last_name
148 | field :email
149 | field :password
150 | field :password_confirmation
151 | field :role
152 | field :company
153 | end
154 |
155 | show do
156 | field :id
157 | field :first_name
158 | field :last_name
159 | field :email
160 | field :role
161 | field :last_sign_in_at
162 | field :company
163 | end
164 | end
165 | # rubocop:enable Metrics/BlockLength
166 | # :nocov:
167 | end
168 |
--------------------------------------------------------------------------------
/app/views/devise/confirmations/new.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 | <%= image_tag('logo.svg', width: '200px', alt: 'Logo' ) %>
4 |
5 |
6 | <%= t('.resend_confirmation_instructions') %>
7 |
8 | <%= form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f| %>
9 | <%= render "devise/shared/error_messages", resource: resource %>
10 |
11 |
12 | <%= f.label :email %>
13 | <%= f.email_field :email, autofocus: true, autocomplete: "email", value: (resource.pending_reconfirmation? ? resource.unconfirmed_email : resource.email) %>
14 |
15 |
16 |
17 | <%= f.submit t('.resend_confirmation_instructions') %>
18 |
19 | <% end %>
20 |
21 | <%= render "devise/shared/links" %>
22 |
23 |
--------------------------------------------------------------------------------
/app/views/devise/mailer/confirmation_instructions.html.erb:
--------------------------------------------------------------------------------
1 | Welcome <%= @email %>!
2 |
3 | You can confirm your account email through the link below:
4 |
5 | <%= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token) %>
6 |
--------------------------------------------------------------------------------
/app/views/devise/mailer/email_changed.html.erb:
--------------------------------------------------------------------------------
1 | Hello <%= @email %>!
2 |
3 | <% if @resource.try(:unconfirmed_email?) %>
4 | We're contacting you to notify you that your email is being changed to <%= @resource.unconfirmed_email %>.
5 | <% else %>
6 | We're contacting you to notify you that your email has been changed to <%= @resource.email %>.
7 | <% end %>
8 |
--------------------------------------------------------------------------------
/app/views/devise/mailer/password_change.html.erb:
--------------------------------------------------------------------------------
1 | Hallo <%= @resource.email %>!
2 |
3 | Ihr Passwort wurde geändert.
4 |
--------------------------------------------------------------------------------
/app/views/devise/mailer/reset_password_instructions.html.erb:
--------------------------------------------------------------------------------
1 | Hallo <%= @resource.email %>!
2 |
3 | Jemand hat einen Link angefordert, um dein Passwort zu ändern. Du kannst dies über den untenstehenden Link tun.
4 |
5 | <%= link_to 'Passwort ändern', edit_password_url(@resource, reset_password_token: @token) %>
6 |
7 | Wenn Sie dies nicht initiiert haben, ignorieren Sie bitte diese E-Mail.
8 | Ihr Passwort wird sich erst ändern, wenn Sie auf den obigen Link zugreifen und ein neues erstellen.
9 |
--------------------------------------------------------------------------------
/app/views/devise/mailer/unlock_instructions.html.erb:
--------------------------------------------------------------------------------
1 | <%= t('.greeting', recipient: @resource.email) %>
2 |
3 | <%= t('.message') %>
4 |
5 | <%= t('.instruction') %>
6 |
7 | <%= link_to t('.action'), unlock_url(@resource, unlock_token: @token) %>
8 |
--------------------------------------------------------------------------------
/app/views/devise/passwords/edit.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 | <%= image_tag('logo.svg', width: '200px', alt: 'Logo' ) %>
4 |
5 |
6 | <%= t('.change_your_password') %>
7 |
8 | <%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put }) do |f| %>
9 | <%= render "devise/shared/error_messages", resource: resource %>
10 | <%= f.hidden_field :reset_password_token %>
11 |
12 |
13 | <%= f.label :password, t('.new_password') %>
14 | <% if @minimum_password_length %>
15 | <%= t('devise.shared.minimum_password_length', count: @minimum_password_length) %>
16 | <% end %>
17 | <%= f.password_field :password, autofocus: true, autocomplete: "new-password" %>
18 |
19 |
20 |
21 | <%= f.label :password_confirmation, t('.confirm_new_password') %>
22 | <%= f.password_field :password_confirmation, autocomplete: "new-password" %>
23 |
24 |
25 |
26 | <%= f.submit t('.change_my_password') %>
27 |
28 | <% end %>
29 |
30 | <%= render "devise/shared/links" %>
31 |
32 |
--------------------------------------------------------------------------------
/app/views/devise/passwords/new.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <%= t('.forgot_your_password') %>
3 |
4 | <%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post }) do |f| %>
5 | <%= render "devise/shared/error_messages", resource: resource %>
6 |
7 |
8 | <%= f.label :email %>
9 | <%= f.email_field :email, autofocus: true, autocomplete: "email" %>
10 |
11 |
12 |
13 | <%= f.submit t('.send_me_reset_password_instructions') %>
14 |
15 | <% end %>
16 |
17 | <%= render "devise/shared/links" %>
18 |
19 |
--------------------------------------------------------------------------------
/app/views/devise/registrations/edit.html.erb:
--------------------------------------------------------------------------------
1 | <%# Note that registration is currently not used #%>
2 |
3 |
4 | <%= image_tag('logo.svg', width: '200px', alt: 'Logo' ) %>
5 |
6 |
7 | <%= t('.title', resource: resource.model_name.human) %>
8 |
9 | <%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %>
10 | <%= render "devise/shared/error_messages", resource: resource %>
11 |
12 |
13 | <%= f.label :email %>
14 | <%= f.email_field :email, autofocus: true, autocomplete: "email" %>
15 |
16 |
17 | <% if devise_mapping.confirmable? && resource.pending_reconfirmation? %>
18 | <%= t('.currently_waiting_confirmation_for_email', email: resource.unconfirmed_email) %>
19 | <% end %>
20 |
21 |
22 | <%= f.label :password %> (<%= t('.leave_blank_if_you_don_t_want_to_change_it') %>)
23 | <%= f.password_field :password, autocomplete: "new-password" %>
24 | <% if @minimum_password_length %>
25 |
26 | <%= t('devise.shared.minimum_password_length', count: @minimum_password_length) %>
27 | <% end %>
28 |
29 |
30 |
31 | <%= f.label :password_confirmation %>
32 | <%= f.password_field :password_confirmation, autocomplete: "new-password" %>
33 |
34 |
35 |
36 | <%= f.label :current_password %> (<%= t('.we_need_your_current_password_to_confirm_your_changes') %>)
37 | <%= f.password_field :current_password, autocomplete: "current-password" %>
38 |
39 |
40 |
41 | <%= f.submit t('.update') %>
42 |
43 | <% end %>
44 |
45 | <%= t('.cancel_my_account') %>
46 |
47 | <%= t('.unhappy') %> <%= button_to t('.cancel_my_account'), registration_path(resource_name), data: { confirm: t('.are_you_sure') }, method: :delete %>
48 |
49 | <%= link_to t('devise.shared.links.back'), :back %>
50 |
--------------------------------------------------------------------------------
/app/views/devise/registrations/new.html.erb:
--------------------------------------------------------------------------------
1 | <%# Note that registration is currently not used #%>
2 |
3 |
4 | <%= image_tag('logo.svg', width: '200px', alt: 'Logo' ) %>
5 |
6 |
7 | <%= t('.sign_up') %>
8 |
9 | <%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
10 | <%= render "devise/shared/error_messages", resource: resource %>
11 |
12 |
13 | <%= f.label :email %>
14 | <%= f.email_field :email, autofocus: true, autocomplete: "email" %>
15 |
16 |
17 |
18 | <%= f.label :password %>
19 | <% if @minimum_password_length %>
20 | <%= t('devise.shared.minimum_password_length', count: @minimum_password_length) %>
21 | <% end %>
22 | <%= f.password_field :password, autocomplete: "new-password" %>
23 |
24 |
25 |
26 | <%= f.label :password_confirmation %>
27 | <%= f.password_field :password_confirmation, autocomplete: "new-password" %>
28 |
29 |
30 |
31 | <%= f.submit t('.sign_up') %>
32 |
33 | <% end %>
34 |
35 | <%= render "devise/shared/links" %>
36 |
--------------------------------------------------------------------------------
/app/views/devise/sessions/new.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 | <%= image_tag('logo.svg', width: '200px', alt: 'Logo' ) %>
4 |
5 |
6 | <%= t('.sign_in') %>
7 |
8 | <%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>
9 | <%= render "devise/shared/error_messages", resource: resource %>
10 |
11 |
12 | <%= f.label :email %>
13 | <%= f.email_field :email, autofocus: true, autocomplete: "email" %>
14 |
15 |
16 |
17 | <%= f.label :password %>
18 | <%= f.password_field :password, autocomplete: "current-password" %>
19 |
20 |
21 | <% if devise_mapping.rememberable? %>
22 |
23 | <%= f.check_box :remember_me %>
24 | <%= f.label :remember_me %>
25 |
26 | <% end %>
27 |
28 |
29 | <%= f.submit t('.sign_in') %>
30 |
31 | <% end %>
32 |
33 | <%= render "devise/shared/links" %>
34 |
35 |
--------------------------------------------------------------------------------
/app/views/devise/shared/_error_messages.html.erb:
--------------------------------------------------------------------------------
1 | <% if resource.errors.any? %>
2 |
3 |
4 | <%= I18n.t("errors.messages.not_saved",
5 | count: resource.errors.count,
6 | resource: resource.class.model_name.human.downcase)
7 | %>
8 |
9 |
10 | <% resource.errors.full_messages.each do |message| %>
11 | - <%= message %>
12 | <% end %>
13 |
14 |
15 | <% end %>
16 |
17 | <% flash.each do |key, value| %>
18 | <% key = (key == 'alert' || key == 'error') ? 'error' : 'info' %>
19 | <%= content_tag(:div, value, class: "flash flash--#{key}") %>
20 | <% end %>
--------------------------------------------------------------------------------
/app/views/devise/shared/_links.html.erb:
--------------------------------------------------------------------------------
1 | <%- if controller_name != 'sessions' %>
2 | <%= link_to t(".sign_in"), new_session_path(resource_name) %>
3 | <% end %>
4 |
5 | <%# remove registration %>
6 | <%# if devise_mapping.registerable? && controller_name != 'registrations' %>
7 | <%# link_to t(".sign_up"), new_registration_path(resource_name) %>
8 | <%# end %>
9 |
10 | <%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %>
11 | <%= link_to t(".forgot_your_password"), new_password_path(resource_name) %>
12 | <% end %>
13 |
14 | <%- if devise_mapping.confirmable? && controller_name != 'confirmations' %>
15 | <%= link_to t('.didn_t_receive_confirmation_instructions'), new_confirmation_path(resource_name) %>
16 | <% end %>
17 |
18 | <%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %>
19 | <%= link_to t('.didn_t_receive_unlock_instructions'), new_unlock_path(resource_name) %>
20 | <% end %>
21 |
22 | <%- if devise_mapping.omniauthable? %>
23 | <%- resource_class.omniauth_providers.each do |provider| %>
24 | <%= link_to t('.sign_in_with_provider', provider: OmniAuth::Utils.camelize(provider)), omniauth_authorize_path(resource_name, provider) %>
25 | <% end %>
26 | <% end %>
27 |
--------------------------------------------------------------------------------
/app/views/devise/unlocks/new.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 | <%= image_tag('logo.svg', width: '200px', alt: 'Logo' ) %>
4 |
5 |
6 | <%= t('.resend_unlock_instructions') %>
7 |
8 | <%= form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post }) do |f| %>
9 | <%= render "devise/shared/error_messages", resource: resource %>
10 |
11 |
12 | <%= f.label :email %>
13 | <%= f.email_field :email, autofocus: true, autocomplete: "email" %>
14 |
15 |
16 |
17 | <%= f.submit t('.resend_unlock_instructions') %>
18 |
19 | <% end %>
20 |
21 | <%= render "devise/shared/links" %>
22 |
23 |
--------------------------------------------------------------------------------
/app/views/layouts/application.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | SAAS Boilerplate
5 |
6 | <%= csrf_meta_tags %>
7 | <%= action_cable_meta_tag %>
8 |
9 | <%= render 'shared/favicon' %>
10 |
11 |
12 | <%= stylesheet_link_tag 'application', media: 'all' %>
13 | <%# javascript_include_tag 'application', 'data-turbolinks-track' => 'reload' %>
14 |
15 |
16 |
17 | <%= yield %>
18 |
19 |
--------------------------------------------------------------------------------
/app/views/layouts/mailer.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
9 |
10 |
11 | <%= yield %>
12 |
13 |
14 |
--------------------------------------------------------------------------------
/app/views/layouts/mailer.text.erb:
--------------------------------------------------------------------------------
1 | <%= yield %>
2 |
--------------------------------------------------------------------------------
/app/views/layouts/rails_admin/_navigation.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/views/layouts/rails_admin/_secondary_navigation.html.erb:
--------------------------------------------------------------------------------
1 |
13 |
16 |
--------------------------------------------------------------------------------
/app/views/shared/_favicon.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/bin/bundle:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
3 | load Gem.bin_path('bundler', 'bundle')
4 |
--------------------------------------------------------------------------------
/bin/rails:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | begin
3 | load File.expand_path('../spring', __FILE__)
4 | rescue LoadError => e
5 | raise unless e.message.include?('spring')
6 | end
7 | APP_PATH = File.expand_path('../config/application', __dir__)
8 | require_relative '../config/boot'
9 | require 'rails/commands'
10 |
--------------------------------------------------------------------------------
/bin/rake:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | begin
3 | load File.expand_path('../spring', __FILE__)
4 | rescue LoadError => e
5 | raise unless e.message.include?('spring')
6 | end
7 | require_relative '../config/boot'
8 | require 'rake'
9 | Rake.application.run
10 |
--------------------------------------------------------------------------------
/bin/setup:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require 'fileutils'
3 | include FileUtils
4 |
5 | # path to your application root.
6 | APP_ROOT = File.expand_path('..', __dir__)
7 |
8 | def system!(*args)
9 | system(*args) || abort("\n== Command #{args} failed ==")
10 | end
11 |
12 | chdir APP_ROOT do
13 | # This script is a starting point to setup your application.
14 | # Add necessary setup steps to this file.
15 |
16 | puts '== Installing dependencies =='
17 | system! 'gem install bundler --conservative'
18 | system('bundle check') || system!('bundle install')
19 |
20 | # puts "\n== Copying sample files =="
21 | # unless File.exist?('config/database.yml')
22 | # cp 'config/database.yml.sample', 'config/database.yml'
23 | # end
24 |
25 | puts "\n== Preparing database =="
26 | system! 'bin/rails db:setup'
27 |
28 | puts "\n== Removing old logs and tempfiles =="
29 | system! 'bin/rails log:clear tmp:clear'
30 |
31 | puts "\n== Restarting application server =="
32 | system! 'bin/rails restart'
33 | end
34 |
--------------------------------------------------------------------------------
/bin/spring:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | # This file loads spring without using Bundler, in order to be fast.
4 | # It gets overwritten when you run the `spring binstub` command.
5 |
6 | unless defined?(Spring)
7 | require 'rubygems'
8 | require 'bundler'
9 |
10 | lockfile = Bundler::LockfileParser.new(Bundler.default_lockfile.read)
11 | spring = lockfile.specs.detect { |spec| spec.name == "spring" }
12 | if spring
13 | Gem.use_paths Gem.dir, Bundler.bundle_path.to_s, *Gem.path
14 | gem 'spring', spring.version
15 | require 'spring/binstub'
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/bin/update:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require 'fileutils'
3 | include FileUtils
4 |
5 | # path to your application root.
6 | APP_ROOT = File.expand_path('..', __dir__)
7 |
8 | def system!(*args)
9 | system(*args) || abort("\n== Command #{args} failed ==")
10 | end
11 |
12 | chdir APP_ROOT do
13 | # This script is a way to update your development environment automatically.
14 | # Add necessary update steps to this file.
15 |
16 | puts '== Installing dependencies =='
17 | system! 'gem install bundler --conservative'
18 | system('bundle check') || system!('bundle install')
19 |
20 | puts "\n== Updating database =="
21 | system! 'bin/rails db:migrate'
22 |
23 | puts "\n== Removing old logs and tempfiles =="
24 | system! 'bin/rails log:clear tmp:clear'
25 |
26 | puts "\n== Restarting application server =="
27 | system! 'bin/rails restart'
28 | end
29 |
--------------------------------------------------------------------------------
/bitbucket-pipelines.yml:
--------------------------------------------------------------------------------
1 | # This is a sample build configuration for Ruby.
2 | # Check our guides at https://confluence.atlassian.com/x/8r-5Mw for more examples.
3 | # Only use spaces to indent your .yml configuration.
4 | # -----
5 | # You can specify a custom docker image from Docker Hub as your build environment.
6 | image: ruby:2.6.5
7 |
8 | definitions:
9 | steps:
10 | - step: &run-audit
11 | name: Check Vulnerabilities
12 | script:
13 | - gem install bundler-audit
14 | - bundle audit --update
15 | - step: &run-tests
16 | name: Run tests
17 | caches:
18 | - bundler
19 | script:
20 | - export DATABASE_URL=postgresql://test_user:test_user_password@localhost/ci_test
21 | - cp config/database.ci.yml config/database.yml
22 | - bundle install --path vendor/bundle
23 | - bundle exec rake db:setup
24 | - bundle exec rake db:test:prepare
25 | - bundle exec rspec spec
26 | services:
27 | - postgres
28 | caches:
29 | bundler: vendor/bundle
30 | services:
31 | postgres:
32 | image: postgres
33 | environment:
34 | POSTGRES_DB: ci_test
35 | POSTGRES_USER: test_user
36 | POSTGRES_PASSWORD: test_user_password
37 |
38 | pipelines:
39 | pull-requests:
40 | "**":
41 | - step: *run-tests
42 | branches:
43 | develop:
44 | - step: *run-tests
45 | - step:
46 | name: Deploy to Staging
47 | deployment: staging
48 | script:
49 | - git push https://heroku:$HEROKU_API_KEY@git.heroku.com/$HEROKU_APP_NAME.git develop:master
50 | master:
51 | - step: *run-tests
52 | - step:
53 | name: Deploy to Production
54 | deployment: production
55 | script:
56 | - git push https://heroku:$HEROKU_API_KEY@git.heroku.com/$HEROKU_APP_NAME.git master
57 |
--------------------------------------------------------------------------------
/config.ru:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # This file is used by Rack-based servers to start the application.
4 |
5 | require_relative 'config/environment'
6 |
7 | run Rails.application
8 |
--------------------------------------------------------------------------------
/config/application.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative 'boot'
4 |
5 | require 'rails/all'
6 | # Pick the frameworks you want:
7 | # require 'active_model/railtie'
8 | # require 'active_job/railtie'
9 | # require 'active_record/railtie'
10 | # require 'active_storage/engine'
11 | # require 'action_controller/railtie'
12 | # require 'action_mailer/railtie'
13 | # require 'action_view/railtie'
14 | # require 'action_cable/engine'
15 | # require 'rails/test_unit/railtie'
16 |
17 | # We had to require this module manually. No idea why...
18 | require 'search_object'
19 | require 'search_object/plugin/graphql'
20 |
21 | # Require the gems listed in Gemfile, including any gems
22 | # you've limited to :test, :development, or :production.
23 | Bundler.require(*Rails.groups)
24 |
25 | # Base Module for our App
26 | module RailsDeviseGraphql
27 | # Application entry point
28 | class Application < Rails::Application
29 | # Initialize configuration defaults for originally generated Rails version.
30 | config.load_defaults 6.0
31 |
32 | config.autoloader = :classic
33 |
34 | config.autoload_paths << Rails.root.join('lib')
35 | config.eager_load_paths << Rails.root.join('lib')
36 |
37 | # Settings in config/environments/* take precedence over those specified here.
38 | # Application configuration can go into files in config/initializers
39 | # -- all .rb files in that directory are automatically loaded after loading
40 | # the framework and any gems in your application.
41 |
42 | # Only loads a smaller set of middleware suitable for API only apps.
43 | # Middleware like session, flash, cookies can be added back manually.
44 | # Skip views, helpers and assets when generating a new resource.
45 |
46 | config.time_zone = 'Berlin'
47 |
48 | # devise uses this for default from options
49 | config.action_mailer.default_options = { from: ENV['DEVISE_MAILER_FROM'] }
50 | end
51 | end
52 |
--------------------------------------------------------------------------------
/config/boot.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
4 |
5 | require 'bundler/setup' # Set up gems listed in the Gemfile.
6 | require 'bootsnap/setup' # Speed up boot time by caching expensive operations.
7 |
--------------------------------------------------------------------------------
/config/cable.yml:
--------------------------------------------------------------------------------
1 | development:
2 | adapter: async
3 |
4 | test:
5 | adapter: async
6 |
7 | production:
8 | adapter: redis
9 | url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %>
10 | channel_prefix: rails-devise-graphql-production
11 |
--------------------------------------------------------------------------------
/config/credentials.yml.enc:
--------------------------------------------------------------------------------
1 | QDH2QZpHpGTWG7FS0osN0ZNOwyRIbZoeTk/aT6iWIjFGKRy9w5mNpECufZyMG0LxydIOpA1elHvqcD+GhvzkkTzBt3W1o0a2RQlIfzN9Pc8dsOtXVcYRcNr4it9cMEr7OGkHJP6I/KZp9Xe8KF+NF7OmjRdTW0D39vjJUGwvKtA+zv174ema+hW9wFeku5d0V80mCVe8yL51vWG6ZnBGmaWrESJtRL5cFFT4q93ZzzYG39c7Zpn268h8XUgTvkd4teO0+RV4ofhgff7bV3le4vxSqt1Ocq2OFP66+wS67AH4G7TVTQ8MFZHcSATgaw3ajRkUfMIqN7wamiXj8fsz5iKPCrZNLDiIJ9rI65GgdDjuJozxTj+Tqyvs7S0qNNAfS0VA7ieIpqfXT3ogyPXQAjYzXH0CSH10nQ7H--iDeTJFe6e2H7K3Yz--nYtHPdraCkU6xN2U9KgMvQ==
--------------------------------------------------------------------------------
/config/database.ci.yml:
--------------------------------------------------------------------------------
1 | # this file is used in the build process of bitbucket pipelines CI
2 | default: &default
3 | adapter: postgresql
4 | encoding: unicode
5 | # For details on connection pooling, see rails configuration guide
6 | # http://guides.rubyonrails.org/configuring.html#database-pooling
7 | pool: 5
8 | host: localhost
9 |
10 | development:
11 | <<: *default
12 | database: ci_development
13 |
14 | # Warning: The database defined as "test" will be erased and
15 | # re-generated from your development database when you run "rake".
16 | # Do not set this db to the same as development or production.
17 | test:
18 | <<: *default
19 | database: ci_test
20 | username: test_user
21 | password: test_user_password
22 |
23 | production:
24 | <<: *default
25 | database: ci_production
--------------------------------------------------------------------------------
/config/database.yml:
--------------------------------------------------------------------------------
1 | # SQLite version 3.x
2 | # gem install sqlite3
3 | #
4 | # Ensure the SQLite 3 gem is defined in your Gemfile
5 | # gem 'sqlite3'
6 | #
7 | default: &default
8 | adapter: postgresql
9 | timeout: 5000
10 |
11 | development:
12 | <<: *default
13 | database: rails_devise_graphql_development
14 |
15 | # Warning: The database defined as "test" will be erased and
16 | # re-generated from your development database when you run "rake".
17 | # Do not set this db to the same as development or production.
18 | test:
19 | <<: *default
20 | database: db/rails_devise_graphql_test
21 |
22 | # production:
23 | # <<: *default
24 | # database: db/production.sqlite3
25 |
--------------------------------------------------------------------------------
/config/environment.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Load the Rails application.
4 | require_relative 'application'
5 |
6 | # Initialize the Rails application.
7 | Rails.application.initialize!
8 |
--------------------------------------------------------------------------------
/config/environments/development.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | Rails.application.configure do
4 | # Settings specified here will take precedence over those in config/application.rb.
5 |
6 | # In the development environment your application's code is reloaded on
7 | # every request. This slows down response time but is perfect for development
8 | # since you don't have to restart the web server when you make code changes.
9 | config.cache_classes = false
10 |
11 | # Do not eager load code on boot.
12 | config.eager_load = false
13 |
14 | # Show full error reports.
15 | config.consider_all_requests_local = true
16 |
17 | # Enable/disable caching. By default caching is disabled.
18 | # Run rails dev:cache to toggle caching.
19 | if Rails.root.join('tmp/caching-dev.txt').exist?
20 | config.action_controller.perform_caching = true
21 |
22 | config.cache_store = :memory_store
23 | config.public_file_server.headers = {
24 | 'Cache-Control' => "public, max-age=#{2.days.to_i}"
25 | }
26 | else
27 | config.action_controller.perform_caching = false
28 |
29 | config.cache_store = :null_store
30 | end
31 |
32 | # Store uploaded files on the local file system (see config/storage.yml for options)
33 | config.active_storage.service = :local
34 |
35 | config.action_mailer.raise_delivery_errors = true
36 | config.action_mailer.perform_deliveries = false
37 | config.action_mailer.perform_caching = false
38 |
39 | # Print deprecation notices to the Rails logger.
40 | config.active_support.deprecation = :log
41 |
42 | # Raise an error on page load if there are pending migrations.
43 | config.active_record.migration_error = :page_load
44 |
45 | # Highlight code that triggered database queries in logs.
46 | config.active_record.verbose_query_logs = true
47 |
48 | # Raises error for missing translations
49 | # config.action_view.raise_on_missing_translations = true
50 |
51 | # Use an evented file watcher to asynchronously detect changes in source code,
52 | # routes, locales, etc. This feature depends on the listen gem.
53 | config.file_watcher = ActiveSupport::EventedFileUpdateChecker
54 |
55 | config.action_mailer.default_url_options = { host: ENV['DEFAULT_URL'] }
56 | end
57 |
--------------------------------------------------------------------------------
/config/environments/production.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | Rails.application.configure do
4 | # Settings specified here will take precedence over those in config/application.rb.
5 |
6 | # Code is not reloaded between requests.
7 | config.cache_classes = true
8 |
9 | # Eager load code on boot. This eager loads most of Rails and
10 | # your application in memory, allowing both threaded web servers
11 | # and those relying on copy on write to perform better.
12 | # Rake tasks automatically ignore this option for performance.
13 | config.eager_load = true
14 |
15 | # Full error reports are disabled and caching is turned on.
16 | config.consider_all_requests_local = false
17 | config.action_controller.perform_caching = true
18 |
19 | # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"]
20 | # or in config/master.key. This key is used to decrypt credentials (and other encrypted files).
21 | # config.require_master_key = true
22 |
23 | # Disable serving static files from the `/public` folder by default since
24 | # Apache or NGINX already handles this.
25 | config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present?
26 |
27 | # Enable serving of images, stylesheets, and JavaScripts from an asset server.
28 | # config.action_controller.asset_host = 'http://assets.example.com'
29 |
30 | # Specifies the header that your server uses for sending files.
31 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache
32 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
33 |
34 | # Store uploaded files on the local file system (see config/storage.yml for options)
35 | config.active_storage.service = :local
36 |
37 | # Mount Action Cable outside main process or domain
38 | # config.action_cable.mount_path = nil
39 | # config.action_cable.url = 'wss://example.com/cable'
40 | # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ]
41 |
42 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
43 | config.force_ssl = true
44 |
45 | # Use the lowest log level to ensure availability of diagnostic information
46 | # when problems arise.
47 | config.log_level = :debug
48 |
49 | # Prepend all log lines with the following tags.
50 | config.log_tags = [:request_id]
51 |
52 | # Use a different cache store in production.
53 | # config.cache_store = :mem_cache_store
54 |
55 | # Use a real queuing backend for Active Job (and separate queues per environment)
56 | # config.active_job.queue_adapter = :resque
57 | # config.active_job.queue_name_prefix = "rails-devise-graphql_#{Rails.env}"
58 |
59 | config.action_mailer.perform_caching = false
60 |
61 | # Ignore bad email addresses and do not raise email delivery errors.
62 | # Set this to true and configure the email server for immediate delivery to raise delivery errors.
63 | config.action_mailer.raise_delivery_errors = false
64 | config.action_mailer.perform_deliveries = true
65 | config.action_mailer.delivery_method = :smtp
66 | config.action_mailer.smtp_settings = {
67 | address: ENV['SMTP_ADDRESS'],
68 | port: ENV['SMTP_PORT'].to_i,
69 | domain: ENV['SMTP_DOMAIN'],
70 | user_name: ENV['SMTP_USERNAME'],
71 | password: ENV['SMTP_PASSWORD'],
72 | authentication: ENV['SMTP_AUTH'],
73 | enable_starttls_auto: ENV['SMTP_ENABLE_STARTTLS_AUTO'] == 'true'
74 | }
75 | config.action_mailer.default_url_options = { host: ENV['DEFAULT_URL'] }
76 |
77 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
78 | # the I18n.default_locale when a translation cannot be found).
79 | config.i18n.fallbacks = true
80 |
81 | # Send deprecation notices to registered listeners.
82 | config.active_support.deprecation = :notify
83 |
84 | # Use default logging formatter so that PID and timestamp are not suppressed.
85 | config.log_formatter = ::Logger::Formatter.new
86 |
87 | # Use a different logger for distributed setups.
88 | # require 'syslog/logger'
89 | # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name')
90 |
91 | if ENV['RAILS_LOG_TO_STDOUT'].present?
92 | logger = ActiveSupport::Logger.new($stdout)
93 | logger.formatter = config.log_formatter
94 | config.logger = ActiveSupport::TaggedLogging.new(logger)
95 | end
96 |
97 | # Do not dump schema after migrations.
98 | config.active_record.dump_schema_after_migration = false
99 | end
100 |
--------------------------------------------------------------------------------
/config/environments/test.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | Rails.application.configure do
4 | # Settings specified here will take precedence over those in config/application.rb.
5 |
6 | # The test environment is used exclusively to run your application's
7 | # test suite. You never need to work with it otherwise. Remember that
8 | # your test database is "scratch space" for the test suite and is wiped
9 | # and recreated between test runs. Don't rely on the data there!
10 | config.cache_classes = true
11 |
12 | # Do not eager load code on boot. This avoids loading your whole application
13 | # just for the purpose of running a single test. If you are using a tool that
14 | # preloads Rails for running tests, you may have to set it to true.
15 | config.eager_load = false
16 |
17 | # Configure public file server for tests with Cache-Control for performance.
18 | config.public_file_server.enabled = true
19 | config.public_file_server.headers = {
20 | 'Cache-Control' => "public, max-age=#{1.hour.to_i}"
21 | }
22 |
23 | # Show full error reports and disable caching.
24 | config.consider_all_requests_local = true
25 | config.action_controller.perform_caching = false
26 |
27 | # Raise exceptions instead of rendering exception templates.
28 | config.action_dispatch.show_exceptions = false
29 |
30 | # Disable request forgery protection in test environment.
31 | config.action_controller.allow_forgery_protection = false
32 |
33 | # Store uploaded files on the local file system in a temporary directory
34 | config.active_storage.service = :test
35 |
36 | config.action_mailer.perform_caching = false
37 |
38 | # Tell Action Mailer not to deliver emails to the real world.
39 | # The :test delivery method accumulates sent emails in the
40 | # ActionMailer::Base.deliveries array.
41 | config.action_mailer.delivery_method = :test
42 |
43 | # Print deprecation notices to the stderr.
44 | config.active_support.deprecation = :stderr
45 |
46 | # Raises error for missing translations
47 | # config.action_view.raise_on_missing_translations = true
48 | end
49 |
--------------------------------------------------------------------------------
/config/initializers/application_controller_renderer.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | # Be sure to restart your server when you modify this file.
3 |
4 | # ActiveSupport::Reloader.to_prepare do
5 | # ApplicationController.renderer.defaults.merge!(
6 | # http_host: 'example.org',
7 | # https: false
8 | # )
9 | # end
10 |
--------------------------------------------------------------------------------
/config/initializers/backtrace_silencers.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | # Be sure to restart your server when you modify this file.
3 |
4 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
5 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
6 |
7 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
8 | # Rails.backtrace_cleaner.remove_silencers!
9 |
--------------------------------------------------------------------------------
/config/initializers/cors.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Be sure to restart your server when you modify this file.
4 |
5 | # Avoid CORS issues when API is called from the frontend app.
6 | # Handle Cross-Origin Resource Sharing (CORS) in order to accept cross-origin AJAX requests.
7 |
8 | # Read more: https://github.com/cyu/rack-cors
9 |
10 | Rails.application.config.middleware.insert_before 0, Rack::Cors do
11 | allow do
12 | origins ENV['CLIENT_URL'] ? ENV['CLIENT_URL'].split(',').map(&:strip) : '0.0.0.0:8000'
13 |
14 | # origins '*'
15 | resource '*',
16 | headers: :any,
17 | expose: ['Authorization'],
18 | methods: %i[get post options delete put]
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/config/initializers/filter_parameter_logging.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Be sure to restart your server when you modify this file.
4 |
5 | # Configure sensitive parameters which will be filtered from the log file.
6 | Rails.application.config.filter_parameters += %i[password password_confirmation]
7 |
--------------------------------------------------------------------------------
/config/initializers/friendly_id.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # FriendlyId Global Configuration
4 | #
5 | # Use this to set up shared configuration options for your entire application.
6 | # Any of the configuration options shown here can also be applied to single
7 | # models by passing arguments to the `friendly_id` class method or defining
8 | # methods in your model.
9 | #
10 | # To learn more, check out the guide:
11 | #
12 | # http://norman.github.io/friendly_id/file.Guide.html
13 | FriendlyId.defaults do |config|
14 | # ## Reserved Words
15 | #
16 | # Some words could conflict with Rails's routes when used as slugs, or are
17 | # undesirable to allow as slugs. Edit this list as needed for your app.
18 | config.use :reserved
19 |
20 | config.reserved_words = %w[new edit index session login logout users admin
21 | stylesheets assets javascripts images]
22 |
23 | # ## Friendly Finders
24 | #
25 | # Uncomment this to use friendly finders in all models. By default, if
26 | # you wish to find a record by its friendly id, you must do:
27 | #
28 | # MyModel.friendly.find('foo')
29 | #
30 | # If you uncomment this, you can do:
31 | #
32 | # MyModel.find('foo')
33 | #
34 | # This is significantly more convenient but may not be appropriate for
35 | # all applications, so you must explicity opt-in to this behavior. You can
36 | # always also configure it on a per-model basis if you prefer.
37 | #
38 | # Something else to consider is that using the :finders addon boosts
39 | # performance because it will avoid Rails-internal code that makes runtime
40 | # calls to `Module.extend`.
41 | #
42 | # config.use :finders
43 | #
44 | # ## Slugs
45 | #
46 | # Most applications will use the :slugged module everywhere. If you wish
47 | # to do so, uncomment the following line.
48 | #
49 | # config.use :slugged
50 | #
51 | # By default, FriendlyId's :slugged addon expects the slug column to be named
52 | # 'slug', but you can change it if you wish.
53 | #
54 | # config.slug_column = 'slug'
55 | #
56 | # When FriendlyId can not generate a unique ID from your base method, it appends
57 | # a UUID, separated by a single dash. You can configure the character used as the
58 | # separator. If you're upgrading from FriendlyId 4, you may wish to replace this
59 | # with two dashes.
60 | #
61 | # config.sequence_separator = '-'
62 | #
63 | # ## Tips and Tricks
64 | #
65 | # ### Controlling when slugs are generated
66 | #
67 | # As of FriendlyId 5.0, new slugs are generated only when the slug field is
68 | # nil, but if you're using a column as your base method can change this
69 | # behavior by overriding the `should_generate_new_friendly_id` method that
70 | # FriendlyId adds to your model. The change below makes FriendlyId 5.0 behave
71 | # more like 4.0.
72 | #
73 | # config.use Module.new {
74 | # def should_generate_new_friendly_id?
75 | # slug.blank? || _changed?
76 | # end
77 | # }
78 | #
79 | # FriendlyId uses Rails's `parameterize` method to generate slugs, but for
80 | # languages that don't use the Roman alphabet, that's not usually sufficient.
81 | # Here we use the Babosa library to transliterate Russian Cyrillic slugs to
82 | # ASCII. If you use this, don't forget to add "babosa" to your Gemfile.
83 | #
84 | # config.use Module.new {
85 | # def normalize_friendly_id(text)
86 | # text.to_slug.normalize! :transliterations => [:russian, :latin]
87 | # end
88 | # }
89 | end
90 |
--------------------------------------------------------------------------------
/config/initializers/generators.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | Rails.application.config.generators do |g|
4 | g.orm :active_record, primary_key_type: :uuid
5 | g.orm :active_record, foreign_key_type: :uuid
6 | end
7 |
--------------------------------------------------------------------------------
/config/initializers/graphql_auth.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | GraphQL::Auth.configure do |config|
4 | config.token_lifespan = 4.hours
5 | config.jwt_secret_key = ENV['DEVISE_SECRET_KEY']
6 | config.app_url = ENV['CLIENT_URL']
7 |
8 | config.user_type = '::Types::Users::UserType'
9 |
10 | # Devise allowed actions
11 | # Don't forget to enable the lockable setting in your Devise user model if you plan on using the lock_account feature
12 | config.allow_sign_up = true
13 | config.allow_lock_account = true
14 | config.allow_unlock_account = true
15 | config.allow_email_confirmable = true
16 |
17 | # Allow custom mutations for signup and update account
18 | # config.sign_up_mutation = '::Mutations::Auth::SignUp'
19 | # config.update_account_mutation = '::Mutations::Auth::UpdateAccount'
20 | end
21 |
--------------------------------------------------------------------------------
/config/initializers/graphql_errors.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | GraphQL::Errors.configure(GraphqlSchema) do
4 | rescue_from ActiveRecord::RecordNotFound do |_exception|
5 | nil
6 | end
7 |
8 | rescue_from CanCan::AccessDenied do |exception|
9 | GraphQL::ExecutionError.new(exception.message)
10 | end
11 |
12 | rescue_from ActiveRecord::RecordInvalid do |exception|
13 | GraphQL::ExecutionError.new(exception.record.errors.full_messages.join("\n"))
14 | end
15 |
16 | # rescue_from StandardError do |exception|
17 | # GraphQL::ExecutionError.new("Please try to execute the query for this field later")
18 | # end
19 |
20 | # rescue_from CustomError do |exception, object, arguments, context|
21 | # error = GraphQL::ExecutionError.new("Error found!")
22 | # firstError.path = context.path + ["myError"]
23 | # context.add_error(firstError)
24 | # end
25 | end
26 |
--------------------------------------------------------------------------------
/config/initializers/inflections.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | # Be sure to restart your server when you modify this file.
3 |
4 | # Add new inflection rules using the following format. Inflections
5 | # are locale specific, and you may define rules for as many different
6 | # locales as you wish. All of these examples are active by default:
7 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
8 | # inflect.plural /^(ox)$/i, '\1en'
9 | # inflect.singular /^(ox)en/i, '\1'
10 | # inflect.irregular 'person', 'people'
11 | # inflect.uncountable %w( fish sheep )
12 | # end
13 |
14 | # These inflection rules are supported but not enabled by default:
15 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
16 | # inflect.acronym 'RESTful'
17 | # end
18 |
--------------------------------------------------------------------------------
/config/initializers/locale.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Whitelist locales available for the application
4 | I18n.available_locales = %i[en de]
5 |
6 | # Set default locale to something other than :en
7 | I18n.default_locale = :en
8 |
9 | I18n.load_path += Dir[Rails.root.join('config/locales/**/*yml').to_s]
10 |
--------------------------------------------------------------------------------
/config/initializers/mime_types.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | # Be sure to restart your server when you modify this file.
3 |
4 | # Add new mime types for use in respond_to blocks:
5 | # Mime::Type.register "text/richtext", :rtf
6 |
--------------------------------------------------------------------------------
/config/initializers/new_framework_defaults_6_0.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | # Be sure to restart your server when you modify this file.
3 | #
4 | # This file contains migration options to ease your Rails 6.0 upgrade.
5 | #
6 | # Once upgraded flip defaults one by one to migrate to the new default.
7 | #
8 | # Read the Guide for Upgrading Ruby on Rails for more info on each option.
9 |
10 | # Don't force requests from old versions of IE to be UTF-8 encoded.
11 | # Rails.application.config.action_view.default_enforce_utf8 = false
12 |
13 | # Embed purpose and expiry metadata inside signed and encrypted
14 | # cookies for increased security.
15 | #
16 | # This option is not backwards compatible with earlier Rails versions.
17 | # It's best enabled when your entire app is migrated and stable on 6.0.
18 | # Rails.application.config.action_dispatch.use_cookies_with_metadata = true
19 |
20 | # Change the return value of `ActionDispatch::Response#content_type` to Content-Type header without modification.
21 | # Rails.application.config.action_dispatch.return_only_media_type_on_content_type = false
22 |
23 | # Return false instead of self when enqueuing is aborted from a callback.
24 | # Rails.application.config.active_job.return_false_on_aborted_enqueue = true
25 |
26 | # Send Active Storage analysis and purge jobs to dedicated queues.
27 | # Rails.application.config.active_storage.queues.analysis = :active_storage_analysis
28 | # Rails.application.config.active_storage.queues.purge = :active_storage_purge
29 |
30 | # When assigning to a collection of attachments declared via `has_many_attached`, replace existing
31 | # attachments instead of appending. Use #attach to add new attachments without replacing existing ones.
32 | # Rails.application.config.active_storage.replace_on_assign_to_many = true
33 |
34 | # Use ActionMailer::MailDeliveryJob for sending parameterized and normal mail.
35 | #
36 | # The default delivery jobs (ActionMailer::Parameterized::DeliveryJob, ActionMailer::DeliveryJob),
37 | # will be removed in Rails 6.1. This setting is not backwards compatible with earlier Rails versions.
38 | # If you send mail in the background, job workers need to have a copy of
39 | # MailDeliveryJob to ensure all delivery jobs are processed properly.
40 | # Make sure your entire app is migrated and stable on 6.0 before using this setting.
41 | # Rails.application.config.action_mailer.delivery_job = "ActionMailer::MailDeliveryJob"
42 |
43 | # Enable the same cache key to be reused when the object being cached of type
44 | # `ActiveRecord::Relation` changes by moving the volatile information (max updated at and count)
45 | # of the relation's cache key into the cache version to support recycling cache key.
46 | # Rails.application.config.active_record.collection_cache_versioning = true
47 |
--------------------------------------------------------------------------------
/config/initializers/rack_attack.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | Rack::Attack.enabled = false if ENV['RACK_ATTACK_ENABLED'] == 'false'
4 | # otherwise default ON
5 |
6 | # rubocop:disable Metrics/ClassLength
7 | module Rack
8 | # Define roles for blocking users
9 | class Attack
10 | def self.user_session?(req)
11 | # stored in cookies
12 | if req.env['rack.request.cookie_hash'] && req.env['rack.request.cookie_hash']['user']
13 | user = JSON.parse(req.env['rack.request.cookie_hash']['user'])
14 | return true if user && user['email'].present?
15 | end
16 |
17 | # devise is storing user id in rack.session after a valid authentication
18 | req.env['rack.session'] &&
19 | req.env['rack.session']['warden.user.user.key'] &&
20 | req.env['rack.session']['warden.user.user.key'][0][0]
21 | end
22 |
23 | # Always allow requests from localhost
24 | # (blocklist & throttles are skipped)
25 | Rack::Attack.safelist('allow from localhost') do |req|
26 | # Requests are allowed if the return value is truthy
27 | req.ip == '127.0.0.1' || req.ip == '::1'
28 | end
29 |
30 | # Always allow requests from localhost
31 | # (blocklist & throttles are skipped)
32 | whitelist = ENV['RACK_WHITELIST'] ? ENV['RACK_WHITELIST'].split(',') : []
33 | Rack::Attack.safelist('allow from ENV ATTACK_WHITELIST') do |req|
34 | # Requests are allowed if the return value is truthy
35 | whitelist.include?(req.ip)
36 | end
37 |
38 | # If any single client IP is making tons of requests, then they're
39 | # probably malicious or a poorly-configured scraper. Either way, they
40 | # don't deserve to hog all of the app server's CPU. Cut them off!
41 | #
42 | # Note: If you're serving assets through rack, those requests may be
43 | # counted by rack-attack and this throttle may be activated too
44 | # quickly. If so, enable the condition to exclude them from tracking.
45 |
46 | # Throttle all requests by IP
47 | request_limit = (ENV['ATTACK_REQUEST_LIMIT'] || 300).to_i
48 | request_period = (ENV['ATTACK_REQUEST_PERIOD_IN_MINUTES'] || 5).to_i
49 | ban_time = (ENV['ATTACK_REQUEST_BAN_TIME_IN_MINUTES'] || 30).to_i
50 | (1..3).each do |level|
51 | # level 1 -> 300 requests in 5 minutes (60rpm), ban for 30 minutes
52 | # level 2 -> 600 requests in 25 minutes (24rpm), ban for 60 minutes
53 | # level 3 -> 900 requests in 125 minutes (7.2rpm), ban for 90 minutes
54 | throttle(
55 | "request/ip/#{level}",
56 | limit: request_limit * level,
57 | period: (request_period**level).minutes,
58 | bantime: (ban_time * level).minutes
59 | ) do |req|
60 | req.ip if !req.path.start_with?('/assets') && !Rack::Attack.user_session?(req)
61 | end
62 | end
63 |
64 | # Throttle authenticated requests by IP
65 | request_limit = (ENV['ATTACK_AUTHENTICATED_REQUEST_LIMIT'] || 500).to_i
66 | request_period = (ENV['ATTACK_AUTHENTICATED_REQUEST_PERIOD_IN_MINUTES'] || 5).to_i
67 | ban_time = (ENV['ATTACK_AUTHENTICATED_REQUEST_BAN_TIME_IN_MINUTES'] || 10).to_i
68 | (1..3).each do |level|
69 | # level 1 -> 500 requests in 5 minutes (100rpm), ban for 10 minute
70 | # level 2 -> 1000 requests in 25 minutes (40rpm), ban for 20 minutes
71 | # level 3 -> 1500 requests in 125 minutes (12rpm), ban for 30 minutes
72 | throttle(
73 | "request/authenticated/ip/#{level}",
74 | limit: request_limit * level,
75 | period: (request_period**level).minutes,
76 | bantime: (ban_time * level).minutes
77 | ) do |req|
78 | req.ip if !req.path.start_with?('/assets') && Rack::Attack.user_session?(req)
79 | end
80 | end
81 |
82 | ### Prevent Brute-Force Attacks ###
83 |
84 | # The most common brute-force login attack is a brute-force password
85 | # attack where an attacker simply tries a large number of emails and
86 | # passwords to see if any credentials match.
87 | #
88 | # Another common method of attack is to use a swarm of computers with
89 | # different IPs to try brute-forcing a password for a specific account.
90 |
91 | # Key: "rack::attack:#{Time.now.to_i/:period}:logins/ip:#{req.ip}"
92 | # LOGIN / SIGN UP
93 |
94 | auth_limit = (ENV['ATTACK_AUTH_LIMIT'] || 30).to_i
95 | auth_period = (ENV['ATTACK_AUTH_PERIOD_IN_MINUTES'] || 10).to_i
96 | auth_ban_time = (ENV['ATTACK_AUTH_BAN_TIME_IN_MINUTES'] || 30).to_i
97 |
98 | (1..3).each do |level|
99 | # level 1 -> 30 auth requests in 10 minutes, ban for 30 minutes
100 | # level 2 -> 60 auth requests in 100 minutes, ban for 60 minutes
101 | # level 3 -> 90 auth requests per 1000 minutes (16,5 hours), ban for 120 minutes
102 |
103 | # Devise sign_in
104 | throttle(
105 | "request/devise/ip/#{level}",
106 | limit: auth_limit * level,
107 | period: (auth_period**level).minutes,
108 | bantime: (auth_ban_time * level).minutes
109 | ) do |req|
110 | req.ip if req.path == '/users/sign_in' && (req.post? || req.put?)
111 | end
112 |
113 | # Devise password reset
114 | throttle(
115 | "request/devise/password/ip/#{level}",
116 | limit: auth_limit * level,
117 | period: (auth_period**level).minutes,
118 | bantime: (auth_ban_time * level).minutes
119 | ) do |req|
120 | req.ip if req.path == '/users/password' && (req.post? || req.put?)
121 | end
122 |
123 | # GraphQL login & signup via api
124 | throttle(
125 | "request/graphql/auth/ip/#{level}",
126 | limit: auth_limit * level,
127 | period: (auth_period**level).minutes,
128 | bantime: (auth_ban_time * level).minutes
129 | ) do |req|
130 | if req.path == '/graphql' && req.post? && req.body
131 | params = JSON.parse(req.body.read)
132 | req.body.rewind # needed a rewind after parsing it to JSON
133 | if params['query'].include?('signIn') ||
134 | params['query'].include?('signUp')
135 | req.ip
136 | end
137 | end
138 | end
139 |
140 | # GraphQL password reset
141 | throttle(
142 | "request/graphql/password_reset/ip/#{level}",
143 | limit: auth_limit * level,
144 | period: (auth_period**level).minutes,
145 | bantime: (auth_ban_time * level).minutes
146 | ) do |req|
147 | if req.path == '/graphql' && req.post? && req.body
148 | params = JSON.parse(req.body.read)
149 | req.body.rewind # needed a rewind after parsing it to JSON
150 | req.ip if params['query'].include?('resetPassword')
151 | end
152 | end
153 | end
154 |
155 | # Actions
156 | # Limits actions like locking a user or create a message
157 | public_action_limit = (ENV['ATTACK_PUBLIC_ACTION_LIMIT'] || 30).to_i
158 | public_action_period = (ENV['ATTACK_PUBLIC_ACTION_PERIOD_IN_MINUTES'] || 60).to_i
159 | public_action_ban_time = (ENV['ATTACK_PUBLIC_ACTION_BAN_TIME_IN_MINUTES'] || 30).to_i
160 |
161 | throttle(
162 | 'request/public/action/ip',
163 | limit: public_action_limit,
164 | period: public_action_period.minutes,
165 | bantime: public_action_ban_time.minutes
166 | ) do |req|
167 | if req.path == '/graphql' && req.post? && req.body
168 | params = JSON.parse(req.body.read)
169 | req.body.rewind # needed a rewind after parsing it to JSON
170 | if params['query'].include?('unlockAccount') ||
171 | params['query'].include?('lockAccount') ||
172 | params['query'].include?('createConversation') ||
173 | params['query'].include?('createMessage')
174 | req.ip
175 | end
176 | end
177 | end
178 |
179 | ### Custom Throttle Response ###
180 | # Add a helpful response about the rate limit for clients
181 | # For responses that did not exceed a throttle limit, Rack::Attack annotates the env with match data:
182 | # request.env['rack.attack.throttle_data'][name]
183 | # => { discriminator: d, count: n, period: p, limit: l, epoch_time: t }
184 | self.throttled_response = lambda do |_env|
185 | # match_data = env['rack.attack.match_data']
186 | # now = match_data[:epoch_time]
187 |
188 | headers = {}
189 |
190 | [429, headers, [{ 'errors': [{ 'message': 'Too many requests' }] }.to_json]]
191 | end
192 | end
193 | end
194 |
195 | # Error reporting
196 | ActiveSupport::Notifications.subscribe(/rack_attack/) do |name, start, _finish, _request_id, payload|
197 | # request object available in payload[:request]
198 | if %i[throttle blacklist].include? payload[:request].env['rack.attack.match_type']
199 | error = [
200 | payload[:request].env['rack.attack.match_type'],
201 | name,
202 | start,
203 | payload[:request].ip,
204 | payload[:request].request_method,
205 | payload[:request].fullpath
206 | ].join(' ')
207 | Rails.logger.warn error
208 | # Rollbar.warning(error, payload[:request].env)
209 | end
210 | end
211 | # rubocop:enable Metrics/ClassLength
212 |
--------------------------------------------------------------------------------
/config/initializers/rails_admin.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | RailsAdmin.config do |config|
4 | ### Popular gems integration
5 |
6 | ## == Devise ==
7 | config.parent_controller = 'ApplicationController'
8 |
9 | config.authenticate_with do
10 | warden.authenticate! scope: :user
11 | end
12 | config.current_user_method(&:current_superadmin)
13 |
14 | ## == Cancan ==
15 | config.authorize_with :cancancan
16 |
17 | ## == Pundit ==
18 | # config.authorize_with :pundit
19 |
20 | ## == PaperTrail ==
21 | # config.audit_with :paper_trail, 'User', 'PaperTrail::Version' # PaperTrail >= 3.0.0
22 |
23 | ### More at https://github.com/sferik/rails_admin/wiki/Base-configuration
24 |
25 | ## == Gravatar integration ==
26 | ## To disable Gravatar integration in Navigation Bar set to false
27 | # config.show_gravatar = true
28 |
29 | # config.excluded_models << 'Place'
30 |
31 | config.actions do
32 | dashboard # mandatory
33 | index # mandatory
34 | new
35 | export
36 | bulk_delete
37 | show
38 | edit
39 | delete
40 | # show_in_app
41 |
42 | ## With an audit adapter, you can add:
43 | # history_index
44 | # history_show
45 | end
46 | end
47 |
--------------------------------------------------------------------------------
/config/initializers/wrap_parameters.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Be sure to restart your server when you modify this file.
4 |
5 | # This file contains settings for ActionController::ParamsWrapper which
6 | # is enabled by default.
7 |
8 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
9 | ActiveSupport.on_load(:action_controller) do
10 | wrap_parameters format: [:json]
11 | end
12 |
13 | # To enable root element in JSON for ActiveRecord objects.
14 | # ActiveSupport.on_load(:active_record) do
15 | # self.include_root_in_json = true
16 | # end
17 |
--------------------------------------------------------------------------------
/config/locales/activerecord/de.yml:
--------------------------------------------------------------------------------
1 | de:
2 | attributes:
3 | slug: Pfad
4 | activerecord:
5 | attributes:
6 | company:
7 | name: Name des Unternehmens
8 | users_count: Anzahl Mitarbeiter
9 | user:
10 | confirmation_sent_at: Bestätigung gesendet am
11 | confirmation_token: Bestätigungs-Token
12 | confirmed_at: Bestätigt am
13 | created_at: Erstellt am
14 | current_password: Bisheriges Passwort
15 | current_sign_in_at: Aktuelle Anmeldung vom
16 | current_sign_in_ip: IP der aktuellen Anmeldung
17 | email: E-Mail
18 | encrypted_password: Verschlüsseltes Passwort
19 | failed_attempts: Fehlversuche
20 | last_sign_in_at: Letzte Anmeldung am
21 | last_sign_in_ip: IP der letzten Anmeldung
22 | locked_at: Gesperrt am
23 | password: Passwort
24 | password_confirmation: Passwortbestätigung
25 | remember_created_at: Angemeldet bleiben vom
26 | remember_me: Angemeldet bleiben
27 | reset_password_sent_at: Passwort-Zurücksetzen-E-Mail gesendet am
28 | reset_password_token: Passwort-Zurücksetzen-Token
29 | sign_in_count: Anzahl Anmeldungen
30 | unconfirmed_email: Unbestätigte E-Mail-Adresse
31 | unlock_token: Entsperrungs-Token
32 | updated_at: Aktualisiert am
33 | first_name: Vorname
34 | last_name: Nachname
35 | role: Rolle
36 | models:
37 | user:
38 | one: Benutzer
39 | other: Benutzer
40 | company:
41 | one: Unternehmen
42 | other: Unternehmen
43 |
--------------------------------------------------------------------------------
/config/locales/activerecord/en.yml:
--------------------------------------------------------------------------------
1 | en:
2 | attributes:
3 | slug: Slug
4 | activerecord:
5 | attributes:
6 | company:
7 | name: Name of company
8 | users_count: Number of employees
9 | user:
10 | confirmation_sent_at: Confirmation sent at
11 | confirmation_token: Confirmation token
12 | confirmed_at: Confirmed at
13 | created_at: Created at
14 | current_password: Current password
15 | current_sign_in_at: Current sign in at
16 | current_sign_in_ip: Current sign in IP
17 | email: Email
18 | encrypted_password: Encrypted password
19 | failed_attempts: Failed attempts
20 | last_sign_in_at: Last sign in at
21 | last_sign_in_ip: Last sign in IP
22 | locked_at: Locked at
23 | password: Password
24 | password_confirmation: Password confirmation
25 | remember_created_at: Remember created at
26 | remember_me: Remember me
27 | reset_password_sent_at: Reset password sent at
28 | reset_password_token: Reset password token
29 | sign_in_count: Sign in count
30 | unconfirmed_email: Unconfirmed email
31 | unlock_token: Unlock token
32 | updated_at: Updated at
33 | first_name: Firstname
34 | last_name: Lastname
35 | role: Role
36 | models:
37 | user:
38 | one: User
39 | other: Users
40 | company:
41 | one: Company
42 | other: Companies
43 |
--------------------------------------------------------------------------------
/config/locales/de.yml:
--------------------------------------------------------------------------------
1 | de:
2 | hello: "Hallo Welt"
3 |
--------------------------------------------------------------------------------
/config/locales/devise/invitable/en.yml:
--------------------------------------------------------------------------------
1 | en:
2 | devise:
3 | failure:
4 | invited: "You have a pending invitation, accept it to finish creating your account."
5 | invitations:
6 | send_instructions: "An invitation email has been sent to %{email}."
7 | invitation_token_invalid: "The invitation token provided is not valid!"
8 | updated: "Your password was set successfully. You are now signed in."
9 | updated_not_active: "Your password was set successfully."
10 | no_invitations_remaining: "No invitations remaining"
11 | invitation_removed: "Your invitation was removed."
12 | new:
13 | header: "Send invitation"
14 | submit_button: "Send an invitation"
15 | edit:
16 | header: "Set your password"
17 | submit_button: "Set my password"
18 | mailer:
19 | invitation_instructions:
20 | subject: "Invitation instructions"
21 | hello: "Hello %{email}"
22 | someone_invited_you: "Someone has invited you to %{url}, you can accept it through the link below."
23 | accept: "Accept invitation"
24 | accept_until: "This invitation will be due in %{due_date}."
25 | ignore: "If you don't want to accept the invitation, please ignore this email. Your account won't be created until you access the link above and set your password."
26 | time:
27 | formats:
28 | devise:
29 | mailer:
30 | invitation_instructions:
31 | accept_until_format: "%B %d, %Y %I:%M %p"
32 |
--------------------------------------------------------------------------------
/config/locales/devise/views/de.yml:
--------------------------------------------------------------------------------
1 | de:
2 | devise:
3 | confirmations:
4 | confirmed: Ihre E-Mail-Adresse wurde erfolgreich bestätigt.
5 | new:
6 | resend_confirmation_instructions: Anleitung zur Bestätigung noch mal schicken
7 | send_instructions: Sie erhalten in wenigen Minuten eine E-Mail, mit der Sie Ihre Registrierung bestätigen können.
8 | send_paranoid_instructions: Falls Ihre E-Mail-Adresse in unserer Datenbank existiert, erhalten Sie in wenigen Minuten eine E-Mail, mit der Sie Ihre Registrierung bestätigen können.
9 | failure:
10 | already_authenticated: Sie sind bereits angemeldet.
11 | inactive: Ihr Konto ist noch nicht aktiv.
12 | invalid: "%{authentication_keys} oder Passwort ungültig."
13 | last_attempt: Sie haben noch einen Versuch, bis Ihr Konto gesperrt wird.
14 | locked: Ihr Konto ist gesperrt.
15 | not_found_in_database: "%{authentication_keys} oder Passwort ungültig."
16 | timeout: Ihre Sitzung ist abgelaufen. Bitte melden Sie sich erneut an.
17 | unauthenticated: Sie müssen sich anmelden oder registrieren, bevor Sie fortfahren können.
18 | unconfirmed: Sie müssen Ihr Konto bestätigen, bevor Sie fortfahren können.
19 | invited: "Sie haben eine ausstehende Einladung, nehmen Sie diese an, um die Erstellung Ihres Kontos abzuschließen."
20 | mailer:
21 | confirmation_instructions:
22 | action: Mein Konto bestätigen
23 | greeting: Willkommen %{recipient}!
24 | instruction: "Folgen Sie dem untenstehenden Link, um Ihr Konto zu bestätigen:"
25 | subject: Anleitung zur Bestätigung Ihres Kontos
26 | email_changed:
27 | greeting: Hallo %{recipient}!
28 | message: Wir schreiben Ihnen, um Sie darüber zu informieren, dass Ihre E-Mail-Adresse zu %{email} geändert wurde.
29 | subject: E-Mail-Adresse geändert
30 | password_change:
31 | greeting: Hallo %{recipient}!
32 | message: Wir schreiben Ihnen, um Sie darüber zu informieren, dass Ihr Passwort geändert wurde.
33 | subject: Passwort geändert
34 | reset_password_instructions:
35 | action: Passwort ändern
36 | greeting: Hallo %{recipient}!
37 | instruction: Jemand hat einen Link angefordert, um Ihr Passwort zu ändern. Klicken Sie auf den unten aufgeführten Link um das Passwort zu ändern.
38 | instruction_2: Bitte ignorieren Sie diese E-Mail, wenn Sie kein neues Passwort angefordert haben.
39 | instruction_3: Das Passwort wird nicht geändert bis Sie den obenstehenden Link abrufen und ein neues Passwort bestimmen.
40 | subject: Anleitung für das Zurücksetzen Ihres Passworts
41 | unlock_instructions:
42 | action: Mein Konto entsperren
43 | greeting: Hallo %{recipient}!
44 | instruction: "Folgen Sie dem untenstehenden Link, um Ihr Konto zu entsperren:"
45 | message: Ihr Konto wurde aufgrund einer großen Anzahl von fehlgeschlagenen Anmeldeversuchen gesperrt.
46 | subject: Anleitung für die Konto-Freischaltung
47 | invitation_instructions:
48 | subject: "Ihre Einladung annehmen"
49 | hello: "Hallo %{email}"
50 | someone_invited_you: "Jemand hat Sie zu %{url} eingeladen, akzeptieren Sie die Einladung über den angefügten Link."
51 | accept: "Einladung annehmen"
52 | accept_until: "Diese Einladung ist gültig bis %{due_date}."
53 | ignore: "Wenn Sie die Einladung nicht annehmen möchten, ignorieren Sie diese E-Mail. Ihr Konto wird nicht erstellt, solange sie nicht über den Link Ihr Passwort vergeben."
54 | invitations:
55 | send_instructions: "Eine Einladung wurde versendet an %{email}."
56 | invitation_token_invalid: "Der Token zur Einladung ist nicht gültig!"
57 | updated: "Ihr Passwort wurde erfolgreich gestzt. Sie sind nun eingeloggt."
58 | updated_not_active: "Ihr Passwort wurde erfolgreich gesetzt."
59 | no_invitations_remaining: "Keine Einladungen mehr übrig."
60 | invitation_removed: "Ihre Einladung wurde entfernt."
61 | new:
62 | header: "Einladung versenden"
63 | submit_button: "Versende eine Einladung"
64 | edit:
65 | header: "Vergebe ein Passwort"
66 | submit_button: "Mein Passwort setzen"
67 | omniauth_callbacks:
68 | failure: Sie konnten nicht mit Ihrem %{kind}-Konto angemeldet werden, weil "%{reason}".
69 | success: Sie haben sich erfolgreich mit Ihrem %{kind}-Konto angemeldet.
70 | passwords:
71 | edit:
72 | change_my_password: Ändere mein Passwort
73 | change_your_password: Passwort ändern
74 | confirm_new_password: Neues Passwort bestätigen
75 | new_password: Neues Passwort
76 | new:
77 | forgot_your_password: Haben Sie Ihr Passwort vergessen?
78 | send_me_reset_password_instructions: Schicken Sie mir die Anleitung, mein Passwort zurückzusetzen
79 | no_token: Sie können sich nicht auf dieser Seite anmelden, wenn Sie nicht von einer Passwort-Zurücksetzen-E-Mail kommen. Wenn Sie von solch einer E-Mail kommen, überprüfen Sie bitte, ob Sie die gesamte URL verwendet haben.
80 | send_instructions: Sie erhalten in wenigen Minuten eine E-Mail mit der Anleitung, wie Sie Ihr Passwort zurücksetzen können.
81 | send_paranoid_instructions: Falls Ihre E-Mail-Adresse in unserer Datenbank existiert, erhalten Sie in wenigen Minuten eine E-Mail mit der Anleitung, wie Sie Ihr Passwort zurücksetzen können.
82 | updated: Ihr Passwort wurde geändert. Sie sind jetzt angemeldet.
83 | updated_not_active: Ihr Passwort wurde erfolgreich geändert.
84 | registrations:
85 | destroyed: Ihr Konto wurde gelöscht. Wir hoffen, dass wir Sie bald wiedersehen.
86 | edit:
87 | are_you_sure: Sind Sie sicher?
88 | cancel_my_account: Konto löschen
89 | currently_waiting_confirmation_for_email: Warte auf Bestätigung von %{email}.
90 | leave_blank_if_you_don_t_want_to_change_it: freilassen, wenn Sie das nicht ändern wollen
91 | title: "%{resource} bearbeiten"
92 | unhappy: Unzufrieden?
93 | update: Aktualisieren
94 | we_need_your_current_password_to_confirm_your_changes: wir benötigen Ihr aktuelles Passwort, um die Änderung zu bestätigen
95 | new:
96 | sign_up: Registrieren
97 | signed_up: Sie haben sich erfolgreich registriert.
98 | signed_up_but_inactive: Sie haben sich erfolgreich registriert. Wir konnten Sie jedoch nicht anmelden, weil Ihr Konto noch nicht aktiviert ist.
99 | signed_up_but_locked: Sie haben sich erfolgreich registriert. Wir konnten Sie jedoch nicht anmelden, weil Ihr Konto gesperrt ist.
100 | signed_up_but_unconfirmed: Sie erhalten in wenigen Minuten eine E-Mail mit einem Link für die Bestätigung der Registrierung. Klicken Sie auf den Link um Ihr Konto zu aktivieren.
101 | update_needs_confirmation: Ihre Daten wurden aktualisiert, aber Sie müssen Ihre neue E-Mail-Adresse bestätigen. Sie erhalten in wenigen Minuten eine E-Mail, mit der Sie die Änderung Ihrer E-Mail-Adresse abschließen können.
102 | updated: Ihre Daten wurden aktualisiert.
103 | updated_but_not_signed_in: Ihre Daten wurden aktualisiert, aber da sich Ihr Passwort geändert hat, müssen Sie sich erneut anmelden
104 | sessions:
105 | already_signed_out: Erfolgreich abgemeldet.
106 | new:
107 | sign_in: Anmelden
108 | signed_in: Erfolgreich angemeldet.
109 | signed_out: Erfolgreich abgemeldet.
110 | shared:
111 | links:
112 | back: Zurück
113 | didn_t_receive_confirmation_instructions: Keine Anleitung zur Bestätigung erhalten?
114 | didn_t_receive_unlock_instructions: Keine Anleitung zum Entsperren erhalten?
115 | forgot_your_password: Passwort vergessen?
116 | sign_in: Anmelden
117 | sign_in_with_provider: Mit %{provider} anmelden
118 | sign_up: Registrieren
119 | minimum_password_length:
120 | one: "(mindestens %{count} Zeichen)"
121 | other: "(mindestens %{count} Zeichen)"
122 | unlocks:
123 | new:
124 | resend_unlock_instructions: Anleitung zum Entsperren noch mal schicken
125 | send_instructions: Sie erhalten in wenigen Minuten eine E-Mail mit der Anleitung, wie Sie Ihr Konto entsperren können.
126 | send_paranoid_instructions: Falls Ihre E-Mail-Adresse in unserer Datenbank existiert, erhalten Sie in wenigen Minuten eine E-Mail, mit der Anleitung, wie Sie Ihr Konto entsperren können.
127 | unlocked: Ihr Konto wurde entsperrt. Bitte melden Sie sich an, um fortzufahren.
128 | time:
129 | formats:
130 | devise:
131 | mailer:
132 | invitation_instructions:
133 | accept_until_format: "%B %d, %Y %I:%M %p"
134 |
--------------------------------------------------------------------------------
/config/locales/devise/views/en.yml:
--------------------------------------------------------------------------------
1 | en:
2 | devise:
3 | confirmations:
4 | confirmed: Your email address has been successfully confirmed.
5 | new:
6 | resend_confirmation_instructions: Resend confirmation instructions
7 | send_instructions: You will receive an email with instructions for how to confirm your email address in a few minutes.
8 | send_paranoid_instructions: If your email address exists in our database, you will receive an email with instructions for how to confirm your email address in a few minutes.
9 | failure:
10 | already_authenticated: You are already signed in.
11 | inactive: Your account is not activated yet.
12 | invalid: Invalid %{authentication_keys} or password.
13 | last_attempt: You have one more attempt before your account is locked.
14 | locked: Your account is locked.
15 | not_found_in_database: Invalid %{authentication_keys} or password.
16 | timeout: Your session expired. Please sign in again to continue.
17 | unauthenticated: You need to sign in or sign up before continuing.
18 | unconfirmed: You have to confirm your email address before continuing.
19 | invited: "You have a pending invitation, accept it to finish creating your account."
20 | mailer:
21 | confirmation_instructions:
22 | action: Confirm my account
23 | greeting: Welcome %{recipient}!
24 | instruction: "You can confirm your account email through the link below:"
25 | subject: Confirmation instructions
26 | email_changed:
27 | greeting: Hello %{recipient}!
28 | message: We're contacting you to notify you that your email has been changed to %{email}.
29 | subject: Email Changed
30 | password_change:
31 | greeting: Hello %{recipient}!
32 | message: We're contacting you to notify you that your password has been changed.
33 | subject: Password Changed
34 | reset_password_instructions:
35 | action: Change my password
36 | greeting: Hello %{recipient}!
37 | instruction: Someone has requested a link to change your password. You can do this through the link below.
38 | instruction_2: If you didn't request this, please ignore this email.
39 | instruction_3: Your password won't change until you access the link above and create a new one.
40 | subject: Reset password instructions
41 | unlock_instructions:
42 | action: Unlock my account
43 | greeting: Hello %{recipient}!
44 | instruction: "Click the link below to unlock your account:"
45 | message: Your account has been locked due to an excessive number of unsuccessful sign in attempts.
46 | subject: Unlock instructions
47 | invitation_instructions:
48 | subject: "Invitation instructions"
49 | hello: "Hello %{email}"
50 | someone_invited_you: "Someone has invited you to %{url}, you can accept it through the link below."
51 | accept: "Accept invitation"
52 | accept_until: "This invitation will be due in %{due_date}."
53 | ignore: "If you don't want to accept the invitation, please ignore this email. Your account won't be created until you access the link above and set your password."
54 | omniauth_callbacks:
55 | failure: Could not authenticate you from %{kind} because "%{reason}".
56 | success: Successfully authenticated from %{kind} account.
57 | invitations:
58 | send_instructions: "An invitation email has been sent to %{email}."
59 | invitation_token_invalid: "The invitation token provided is not valid!"
60 | updated: "Your password was set successfully. You are now signed in."
61 | updated_not_active: "Your password was set successfully."
62 | no_invitations_remaining: "No invitations remaining"
63 | invitation_removed: "Your invitation was removed."
64 | new:
65 | header: "Send invitation"
66 | submit_button: "Send an invitation"
67 | edit:
68 | header: "Set your password"
69 | submit_button: "Set my password"
70 | passwords:
71 | edit:
72 | change_my_password: Change my password
73 | change_your_password: Change your password
74 | confirm_new_password: Confirm new password
75 | new_password: New password
76 | new:
77 | forgot_your_password: Forgot your password?
78 | send_me_reset_password_instructions: Send me reset password instructions
79 | no_token: You can't access this page without coming from a password reset email. If you do come from a password reset email, please make sure you used the full URL provided.
80 | send_instructions: You will receive an email with instructions on how to reset your password in a few minutes.
81 | send_paranoid_instructions: If your email address exists in our database, you will receive a password recovery link at your email address in a few minutes.
82 | updated: Your password has been changed successfully. You are now signed in.
83 | updated_not_active: Your password has been changed successfully.
84 | registrations:
85 | destroyed: Bye! Your account has been successfully cancelled. We hope to see you again soon.
86 | edit:
87 | are_you_sure: Are you sure?
88 | cancel_my_account: Cancel my account
89 | currently_waiting_confirmation_for_email: "Currently waiting confirmation for: %{email}"
90 | leave_blank_if_you_don_t_want_to_change_it: leave blank if you don't want to change it
91 | title: Edit %{resource}
92 | unhappy: Unhappy?
93 | update: Update
94 | we_need_your_current_password_to_confirm_your_changes: we need your current password to confirm your changes
95 | new:
96 | sign_up: Sign up
97 | signed_up: Welcome! You have signed up successfully.
98 | signed_up_but_inactive: You have signed up successfully. However, we could not sign you in because your account is not yet activated.
99 | signed_up_but_locked: You have signed up successfully. However, we could not sign you in because your account is locked.
100 | signed_up_but_unconfirmed: A message with a confirmation link has been sent to your email address. Please follow the link to activate your account.
101 | update_needs_confirmation: You updated your account successfully, but we need to verify your new email address. Please check your email and follow the confirmation link to confirm your new email address.
102 | updated: Your account has been updated successfully.
103 | updated_but_not_signed_in: Your account has been updated successfully, but since your password was changed, you need to sign in again
104 | sessions:
105 | already_signed_out: Signed out successfully.
106 | new:
107 | sign_in: Log in
108 | signed_in: Signed in successfully.
109 | signed_out: Signed out successfully.
110 | shared:
111 | links:
112 | back: Back
113 | didn_t_receive_confirmation_instructions: Didn't receive confirmation instructions?
114 | didn_t_receive_unlock_instructions: Didn't receive unlock instructions?
115 | forgot_your_password: Forgot your password?
116 | sign_in: Log in
117 | sign_in_with_provider: Sign in with %{provider}
118 | sign_up: Sign up
119 | minimum_password_length:
120 | one: "(%{count} character minimum)"
121 | other: "(%{count} characters minimum)"
122 | unlocks:
123 | new:
124 | resend_unlock_instructions: Resend unlock instructions
125 | send_instructions: You will receive an email with instructions for how to unlock your account in a few minutes.
126 | send_paranoid_instructions: If your account exists, you will receive an email with instructions for how to unlock it in a few minutes.
127 | unlocked: Your account has been unlocked successfully. Please sign in to continue.
128 | time:
129 | formats:
130 | devise:
131 | mailer:
132 | invitation_instructions:
133 | accept_until_format: "%B %d, %Y %I:%M %p"
134 |
--------------------------------------------------------------------------------
/config/locales/en.yml:
--------------------------------------------------------------------------------
1 | en:
2 | hello: "Hello world"
3 |
--------------------------------------------------------------------------------
/config/locales/errors/de.yml:
--------------------------------------------------------------------------------
1 | de:
2 | errors:
3 | messages:
4 | already_confirmed: wurde bereits bestätigt; bitte versuchen Sie, sich anzumelden
5 | confirmation_period_expired: musste innerhalb von %{period} bestätigt werden, bitte neu anfordern.
6 | expired: ist abgelaufen, bitte neu anfordern
7 | not_found: nicht gefunden
8 | not_locked: ist nicht gesperrt
9 | not_saved:
10 | one: "%{resource} konnte aufgrund eines Fehlers nicht gespeichert werden:"
11 | other: "%{count} Fehler verhinderten das Speichern von %{resource}:"
12 | resource_not_found: "%{resource} nicht gefunden."
13 |
--------------------------------------------------------------------------------
/config/locales/errors/en.yml:
--------------------------------------------------------------------------------
1 | en:
2 | errors:
3 | messages:
4 | already_confirmed: was already confirmed, please try signing in
5 | confirmation_period_expired: needs to be confirmed within %{period}, please request a new one
6 | expired: has expired, please request a new one
7 | not_found: not found
8 | not_locked: was not locked
9 | not_saved:
10 | one: "1 error prohibited this %{resource} from being saved:"
11 | other: "%{count} errors prohibited this %{resource} from being saved:"
12 | resource_not_found: "%{resource} not found."
13 |
--------------------------------------------------------------------------------
/config/puma.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Puma can serve each request in a thread from an internal thread pool.
4 | # The `threads` method setting takes two numbers: a minimum and maximum.
5 | # Any libraries that use thread pools should be configured to match
6 | # the maximum value specified for Puma. Default is set to 5 threads for minimum
7 | # and maximum; this matches the default thread size of Active Record.
8 | #
9 | threads_count = ENV.fetch('RAILS_MAX_THREADS', 5)
10 | threads threads_count, threads_count
11 |
12 | # Specifies the `port` that Puma will listen on to receive requests; default is 3000.
13 | #
14 | port ENV.fetch('PORT', 3000)
15 |
16 | # Specifies the `environment` that Puma will run in.
17 | #
18 | environment ENV.fetch('RAILS_ENV', 'development')
19 |
20 | # Specifies the number of `workers` to boot in clustered mode.
21 | # Workers are forked webserver processes. If using threads and workers together
22 | # the concurrency of the application would be max `threads` * `workers`.
23 | # Workers do not work on JRuby or Windows (both of which do not support
24 | # processes).
25 | #
26 | workers ENV.fetch('WEB_CONCURRENCY', 2)
27 |
28 | # Use the `preload_app!` method when specifying a `workers` number.
29 | # This directive tells Puma to first boot the application and load code
30 | # before forking the application. This takes advantage of Copy On Write
31 | # process behavior so workers use less memory.
32 | #
33 | # preload_app!
34 |
35 | # Allow puma to be restarted by `rails restart` command.
36 | plugin :tmp_restart
37 |
--------------------------------------------------------------------------------
/config/routes.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | Rails.application.routes.draw do
4 | default_url_options host: ENV['DEFAULT_URL']
5 |
6 | mount RailsAdmin::Engine => '/admin', as: 'rails_admin'
7 | post '/graphql', to: 'graphql#execute'
8 | devise_for :users,
9 | controllers: {
10 | confirmations: 'auth/confirmations',
11 | passwords: 'auth/passwords',
12 | invitations: 'auth/invitations'
13 | },
14 | skip: :registrations # skip registration route
15 |
16 | # Just a blank root path
17 | root 'pages#blank'
18 | end
19 |
--------------------------------------------------------------------------------
/config/spring.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | Spring.watch(
4 | '.ruby-version',
5 | '.rbenv-vars',
6 | 'tmp/restart.txt',
7 | 'tmp/caching-dev.txt'
8 | )
9 |
--------------------------------------------------------------------------------
/config/storage.yml:
--------------------------------------------------------------------------------
1 | test:
2 | service: Disk
3 | root: <%= Rails.root.join("tmp/storage") %>
4 |
5 | local:
6 | service: Disk
7 | root: <%= Rails.root.join("storage") %>
8 |
9 | # amazon:
10 | # service: S3
11 | # access_key_id: <%= ENV['AWS_ACCESS_KEY_ID'] %>
12 | # secret_access_key: <%= ENV['AWS_SECRET_ACCESS_KEY'] %>
13 | # bucket: <%= ENV['AWS_S3_BUCKET'] %>
14 | # region: "eu-west-1" # e.g. 'us-east-1'
15 |
16 | # Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key)
17 | # amazon:
18 | # service: S3
19 | # access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
20 | # secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
21 | # region: us-east-1
22 | # bucket: your_own_bucket
23 |
24 | # Remember not to checkin your GCS keyfile to a repository
25 | # google:
26 | # service: GCS
27 | # project: your_project
28 | # credentials: <%= Rails.root.join("path/to/gcs.keyfile") %>
29 | # bucket: your_own_bucket
30 |
31 | # Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key)
32 | # microsoft:
33 | # service: AzureStorage
34 | # storage_account_name: your_account_name
35 | # storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %>
36 | # container: your_container_name
37 |
38 | # mirror:
39 | # service: Mirror
40 | # primary: local
41 | # mirrors: [ amazon, google, microsoft ]
42 |
--------------------------------------------------------------------------------
/db/migrate/20190209162712_enable_extension_for_uuid.rb:
--------------------------------------------------------------------------------
1 | class EnableExtensionForUuid < ActiveRecord::Migration[6.0]
2 | def change
3 | enable_extension 'pgcrypto' unless extension_enabled?('pgcrypto')
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20190209163712_devise_create_users.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class DeviseCreateUsers < ActiveRecord::Migration[6.0]
4 | def change
5 | create_table :users, id: :uuid do |t|
6 |
7 | ## General
8 | t.string 'first_name', null: false, default: ''
9 | t.string 'last_name', null: false, default: ''
10 |
11 | ## Database authenticatable
12 | t.string :email, null: false, default: ''
13 | t.string :encrypted_password, null: false, default: ''
14 |
15 | ## Recoverable
16 | t.string :reset_password_token
17 | t.datetime :reset_password_sent_at
18 |
19 | ## Rememberable
20 | t.datetime :remember_created_at
21 |
22 | ## Trackable
23 | t.integer :sign_in_count, default: 0, null: false
24 | t.datetime :current_sign_in_at
25 | t.datetime :last_sign_in_at
26 | t.inet :current_sign_in_ip
27 | t.inet :last_sign_in_ip
28 |
29 | ## Confirmable
30 | t.string :confirmation_token
31 | t.datetime :confirmed_at
32 | t.datetime :confirmation_sent_at
33 | t.string :unconfirmed_email # Only if using reconfirmable
34 |
35 | ## Lockable
36 | t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
37 | t.string :unlock_token # Only if unlock strategy is :email or :both
38 | t.datetime :locked_at
39 |
40 | ## Refresh token for JWT auth
41 | t.string :refresh_token
42 |
43 | # role attributes (used as enum in user model)
44 | t.integer :role, default: 0, null: false
45 |
46 | t.timestamps null: false
47 |
48 | t.uuid :company_id
49 | end
50 |
51 | add_index :users, :email, unique: true
52 | add_index :users, :reset_password_token, unique: true
53 | add_index :users, :confirmation_token, unique: true
54 | add_index :users, :unlock_token, unique: true
55 | add_index 'users', ['refresh_token'], name: 'index_users_on_refresh_token', unique: true, using: :btree
56 |
57 | end
58 | end
59 |
--------------------------------------------------------------------------------
/db/migrate/20200911092211_create_active_storage_tables.active_storage.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class CreateActiveStorageTables < ActiveRecord::Migration[6.0]
4 | def change
5 | create_table :active_storage_blobs do |t|
6 | t.string :key, null: false
7 | t.string :filename, null: false
8 | t.string :content_type
9 | t.text :metadata
10 | t.bigint :byte_size, null: false
11 | t.string :checksum, null: false
12 | t.datetime :created_at, null: false
13 |
14 | t.index [ :key ], unique: true
15 | end
16 |
17 | create_table :active_storage_attachments do |t|
18 | t.string :name, null: false
19 | t.references :record, null: false, polymorphic: true, index: false
20 | t.references :blob, null: false
21 |
22 | t.datetime :created_at, null: false
23 |
24 | t.index [ :record_type, :record_id, :name, :blob_id ], name: "index_active_storage_attachments_uniqueness", unique: true
25 | t.foreign_key :active_storage_blobs, column: :blob_id
26 | end
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/db/migrate/20200912120337_create_companies.rb:
--------------------------------------------------------------------------------
1 | class CreateCompanies < ActiveRecord::Migration[6.0]
2 | def change
3 | create_table :companies, id: :uuid do |t|
4 | t.string :name
5 | t.integer :users_count
6 | t.timestamps
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/db/migrate/20200912153138_add_slug_to_companies.rb:
--------------------------------------------------------------------------------
1 | class AddSlugToCompanies < ActiveRecord::Migration[6.0]
2 | def change
3 | add_column :companies, :slug, :string
4 | add_index :companies, :slug, unique: true
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/db/migrate/20200912153858_create_friendly_id_slugs.rb:
--------------------------------------------------------------------------------
1 | class CreateFriendlyIdSlugs < ActiveRecord::Migration[6.0]
2 | def change
3 | create_table :friendly_id_slugs do |t|
4 | t.string :slug, :null => false
5 | t.integer :sluggable_id, :null => false
6 | t.string :sluggable_type, :limit => 50
7 | t.string :scope
8 | t.datetime :created_at
9 | end
10 | add_index :friendly_id_slugs, :sluggable_id
11 | add_index :friendly_id_slugs, [:slug, :sluggable_type]
12 | add_index :friendly_id_slugs, [:slug, :sluggable_type, :scope], :unique => true
13 | add_index :friendly_id_slugs, :sluggable_type
14 |
15 | Companies::Company.all.each(&:save)
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/db/migrate/20200920102035_devise_invitable_add_to_users.rb:
--------------------------------------------------------------------------------
1 | class DeviseInvitableAddToUsers < ActiveRecord::Migration[6.0]
2 | def up
3 | change_table :users do |t|
4 | t.string :invitation_token
5 | t.datetime :invitation_created_at
6 | t.datetime :invitation_sent_at
7 | t.datetime :invitation_accepted_at
8 | t.integer :invitation_limit
9 | t.references :invited_by, polymorphic: true
10 | t.integer :invitations_count, default: 0
11 | t.index :invitations_count
12 | t.index :invitation_token, unique: true # for invitable
13 | t.index :invited_by_id
14 | end
15 | end
16 |
17 | def down
18 | change_table :users do |t|
19 | t.remove_references :invited_by, polymorphic: true
20 | t.remove :invitations_count, :invitation_limit, :invitation_sent_at, :invitation_accepted_at, :invitation_token, :invitation_created_at
21 | end
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/db/schema.rb:
--------------------------------------------------------------------------------
1 | # This file is auto-generated from the current state of the database. Instead
2 | # of editing this file, please use the migrations feature of Active Record to
3 | # incrementally modify your database, and then regenerate this schema definition.
4 | #
5 | # This file is the source Rails uses to define your schema when running `rails
6 | # db:schema:load`. When creating a new database, `rails db:schema:load` tends to
7 | # be faster and is potentially less error prone than running all of your
8 | # migrations from scratch. Old migrations may fail to apply correctly if those
9 | # migrations use external dependencies or application code.
10 | #
11 | # It's strongly recommended that you check this file into your version control system.
12 |
13 | ActiveRecord::Schema.define(version: 2020_09_20_102035) do
14 |
15 | # These are extensions that must be enabled in order to support this database
16 | enable_extension "pgcrypto"
17 | enable_extension "plpgsql"
18 |
19 | create_table "active_storage_attachments", force: :cascade do |t|
20 | t.string "name", null: false
21 | t.string "record_type", null: false
22 | t.bigint "record_id", null: false
23 | t.bigint "blob_id", null: false
24 | t.datetime "created_at", null: false
25 | t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id"
26 | t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true
27 | end
28 |
29 | create_table "active_storage_blobs", force: :cascade do |t|
30 | t.string "key", null: false
31 | t.string "filename", null: false
32 | t.string "content_type"
33 | t.text "metadata"
34 | t.bigint "byte_size", null: false
35 | t.string "checksum", null: false
36 | t.datetime "created_at", null: false
37 | t.index ["key"], name: "index_active_storage_blobs_on_key", unique: true
38 | end
39 |
40 | create_table "companies", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
41 | t.string "name"
42 | t.integer "users_count"
43 | t.datetime "created_at", precision: 6, null: false
44 | t.datetime "updated_at", precision: 6, null: false
45 | t.string "slug"
46 | t.index ["slug"], name: "index_companies_on_slug", unique: true
47 | end
48 |
49 | create_table "conversation_memberships", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
50 | t.uuid "user_id"
51 | t.uuid "conversation_id"
52 | t.uuid "last_read_message_id"
53 | t.datetime "created_at", precision: 6, null: false
54 | t.datetime "updated_at", precision: 6, null: false
55 | t.index ["user_id", "conversation_id"], name: "index_user_id_on_conversation_id", unique: true
56 | end
57 |
58 | create_table "conversations", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
59 | t.string "name"
60 | t.uuid "owner_id"
61 | t.uuid "company_id"
62 | t.datetime "created_at", precision: 6, null: false
63 | t.datetime "updated_at", precision: 6, null: false
64 | end
65 |
66 | create_table "friendly_id_slugs", force: :cascade do |t|
67 | t.string "slug", null: false
68 | t.integer "sluggable_id", null: false
69 | t.string "sluggable_type", limit: 50
70 | t.string "scope"
71 | t.datetime "created_at"
72 | t.index ["slug", "sluggable_type", "scope"], name: "index_friendly_id_slugs_on_slug_and_sluggable_type_and_scope", unique: true
73 | t.index ["slug", "sluggable_type"], name: "index_friendly_id_slugs_on_slug_and_sluggable_type"
74 | t.index ["sluggable_id"], name: "index_friendly_id_slugs_on_sluggable_id"
75 | t.index ["sluggable_type"], name: "index_friendly_id_slugs_on_sluggable_type"
76 | end
77 |
78 | create_table "messages", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
79 | t.uuid "conversation_id"
80 | t.uuid "user_id"
81 | t.text "body"
82 | t.datetime "created_at", precision: 6, null: false
83 | t.datetime "updated_at", precision: 6, null: false
84 | end
85 |
86 | create_table "notifications", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
87 | t.string "notification_type"
88 | t.uuid "initiator_id"
89 | t.uuid "receiver_id"
90 | t.boolean "recent", default: true
91 | t.boolean "is_read", default: false
92 | t.boolean "is_delivered", default: false
93 | t.uuid "resource_id"
94 | t.string "resource_type"
95 | t.datetime "created_at", precision: 6, null: false
96 | t.datetime "updated_at", precision: 6, null: false
97 | t.index ["initiator_id"], name: "index_notifications_on_initiator_id"
98 | t.index ["notification_type"], name: "index_notifications_on_notification_type"
99 | t.index ["receiver_id"], name: "index_notifications_on_receiver_id"
100 | t.index ["resource_id", "resource_type"], name: "index_notifications_on_resource_id_and_resource_type"
101 | end
102 |
103 | create_table "users", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
104 | t.string "first_name", default: "", null: false
105 | t.string "last_name", default: "", null: false
106 | t.string "email", default: "", null: false
107 | t.string "encrypted_password", default: "", null: false
108 | t.string "reset_password_token"
109 | t.datetime "reset_password_sent_at"
110 | t.datetime "remember_created_at"
111 | t.integer "sign_in_count", default: 0, null: false
112 | t.datetime "current_sign_in_at"
113 | t.datetime "last_sign_in_at"
114 | t.inet "current_sign_in_ip"
115 | t.inet "last_sign_in_ip"
116 | t.string "confirmation_token"
117 | t.datetime "confirmed_at"
118 | t.datetime "confirmation_sent_at"
119 | t.string "unconfirmed_email"
120 | t.integer "failed_attempts", default: 0, null: false
121 | t.string "unlock_token"
122 | t.datetime "locked_at"
123 | t.string "refresh_token"
124 | t.integer "role", default: 0, null: false
125 | t.datetime "created_at", precision: 6, null: false
126 | t.datetime "updated_at", precision: 6, null: false
127 | t.uuid "company_id"
128 | t.datetime "last_seen_at"
129 | t.string "invitation_token"
130 | t.datetime "invitation_created_at"
131 | t.datetime "invitation_sent_at"
132 | t.datetime "invitation_accepted_at"
133 | t.integer "invitation_limit"
134 | t.string "invited_by_type"
135 | t.bigint "invited_by_id"
136 | t.integer "invitations_count", default: 0
137 | t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true
138 | t.index ["email"], name: "index_users_on_email", unique: true
139 | t.index ["invitation_token"], name: "index_users_on_invitation_token", unique: true
140 | t.index ["invitations_count"], name: "index_users_on_invitations_count"
141 | t.index ["invited_by_id"], name: "index_users_on_invited_by_id"
142 | t.index ["invited_by_type", "invited_by_id"], name: "index_users_on_invited_by_type_and_invited_by_id"
143 | t.index ["refresh_token"], name: "index_users_on_refresh_token", unique: true
144 | t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
145 | t.index ["unlock_token"], name: "index_users_on_unlock_token", unique: true
146 | end
147 |
148 | add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"
149 | end
150 |
--------------------------------------------------------------------------------
/db/seeds.rb:
--------------------------------------------------------------------------------
1 | user = User.create(
2 | email: ENV['ADMIN_EMAIL'],
3 | password: ENV['ADMIN_PASSWORD'],
4 | password_confirmation: ENV['ADMIN_PASSWORD'],
5 | first_name: ENV['ADMIN_FIRST_NAME'],
6 | last_name: ENV['ADMIN_LAST_NAME'],
7 | role: 'superadmin'
8 | )
9 |
10 | if user
11 | Rails.logger.info "Login with #{ENV['ADMIN_EMAIL']} and #{ENV['ADMIN_PASSWORD']}"
12 | end
13 |
14 | # Create a admin user
15 | admin = User.create(
16 | email: Faker::Internet.email,
17 | password: ENV['ADMIN_PASSWORD'],
18 | password_confirmation: ENV['ADMIN_PASSWORD'],
19 | first_name: Faker::Name.first_name,
20 | last_name: Faker::Name.last_name,
21 | role: 'admin'
22 | )
23 |
24 | User.create(
25 | email: Faker::Internet.email,
26 | password: ENV['ADMIN_PASSWORD'],
27 | password_confirmation: ENV['ADMIN_PASSWORD'],
28 | first_name: Faker::Name.first_name,
29 | last_name: Faker::Name.last_name,
30 | role: 'user',
31 | company_id: admin.company_id
32 | )
33 |
34 | User.create(
35 | email: Faker::Internet.email,
36 | password: ENV['ADMIN_PASSWORD'],
37 | password_confirmation: ENV['ADMIN_PASSWORD'],
38 | first_name: Faker::Name.first_name,
39 | last_name: Faker::Name.last_name,
40 | role: 'user',
41 | company_id: admin.company_id
42 | )
43 |
--------------------------------------------------------------------------------
/env_sample:
--------------------------------------------------------------------------------
1 | DEFAULT_URL=0.0.0.0:3000
2 | CLIENT_URL=0.0.0.0:8000
3 | WEB_CONCURRENCY=2
4 | RAILS_MAX_THREADS=5
5 | ADMIN_EMAIL=demo@zauberware.com
6 | ADMIN_PASSWORD=demo1234
7 | ADMIN_FIRST_NAME=John
8 | ADMIN_LAST_NAME=Doe
9 | DEVISE_SECRET_KEY=replace-this-key-with-a-secret
10 | DEVISE_MAILER_FROM=test@domain.com
11 | IS_HTTP_AUTH_PROTECTED=false
12 | HTTP_AUTH_USER=demo
13 | HTTP_AUTH_PASSWORD=demo1234
14 | SMTP_ADDRESS=
15 | SMTP_PORT=
16 | SMTP_DOMAIN=
17 | SMTP_USERNAME=
18 | SMTP_PASSWORD=
19 | SMTP_AUTH=login
20 | SMTP_ENABLE_STARTTLS_AUTO=true
21 | RACK_ATTACK_ENABLED=false
--------------------------------------------------------------------------------
/lib/regex.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Regex
4 | class Email
5 | VALIDATE = /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i.freeze
6 | VALIDATE_OR_EMPTY = /(^$|#{VALIDATE})/i.freeze
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/lib/tasks/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zauberware/rails-devise-graphql/f3e71d39e9e5bb7d282af4546c65ccead6c67fb0/lib/tasks/.keep
--------------------------------------------------------------------------------
/lib/tasks/auto_annotate_models.rake:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # NOTE: only doing this in development as some production environments (Heroku)
4 | # NOTE: are sensitive to local FS writes, and besides -- it's just not proper
5 | # NOTE: to have a dev-mode tool do its thing in production.
6 | if Rails.env.development?
7 | require 'annotate'
8 | task set_annotation_options: :environment do
9 | # You can override any of these by setting an environment variable of the
10 | # same name.
11 | Annotate.set_defaults(
12 | 'active_admin' => 'false',
13 | 'additional_file_patterns' => [],
14 | 'routes' => 'false',
15 | 'models' => 'true',
16 | 'position_in_routes' => 'before',
17 | 'position_in_class' => 'before',
18 | 'position_in_test' => 'before',
19 | 'position_in_fixture' => 'before',
20 | 'position_in_factory' => 'before',
21 | 'position_in_serializer' => 'before',
22 | 'show_foreign_keys' => 'true',
23 | 'show_complete_foreign_keys' => 'false',
24 | 'show_indexes' => 'true',
25 | 'simple_indexes' => 'false',
26 | 'model_dir' => 'app/models',
27 | 'root_dir' => '',
28 | 'include_version' => 'false',
29 | 'require' => '',
30 | 'exclude_tests' => 'false',
31 | 'exclude_fixtures' => 'false',
32 | 'exclude_factories' => 'false',
33 | 'exclude_serializers' => 'false',
34 | 'exclude_scaffolds' => 'true',
35 | 'exclude_controllers' => 'true',
36 | 'exclude_helpers' => 'true',
37 | 'exclude_sti_subclasses' => 'false',
38 | 'ignore_model_sub_dir' => 'false',
39 | 'ignore_columns' => nil,
40 | 'ignore_routes' => nil,
41 | 'ignore_unknown_models' => 'false',
42 | 'hide_limit_column_types' => 'integer,bigint,boolean',
43 | 'hide_default_column_types' => 'json,jsonb,hstore',
44 | 'skip_on_db_migrate' => 'false',
45 | 'format_bare' => 'true',
46 | 'format_rdoc' => 'false',
47 | 'format_yard' => 'false',
48 | 'format_markdown' => 'false',
49 | 'sort' => 'false',
50 | 'force' => 'false',
51 | 'frozen' => 'false',
52 | 'classified_sort' => 'true',
53 | 'trace' => 'false',
54 | 'wrapper_open' => nil,
55 | 'wrapper_close' => nil,
56 | 'with_comment' => 'true'
57 | )
58 | end
59 |
60 | Annotate.load_tasks
61 | end
62 |
--------------------------------------------------------------------------------
/log/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zauberware/rails-devise-graphql/f3e71d39e9e5bb7d282af4546c65ccead6c67fb0/log/.keep
--------------------------------------------------------------------------------
/public/android-icon-144x144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zauberware/rails-devise-graphql/f3e71d39e9e5bb7d282af4546c65ccead6c67fb0/public/android-icon-144x144.png
--------------------------------------------------------------------------------
/public/android-icon-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zauberware/rails-devise-graphql/f3e71d39e9e5bb7d282af4546c65ccead6c67fb0/public/android-icon-192x192.png
--------------------------------------------------------------------------------
/public/android-icon-36x36.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zauberware/rails-devise-graphql/f3e71d39e9e5bb7d282af4546c65ccead6c67fb0/public/android-icon-36x36.png
--------------------------------------------------------------------------------
/public/android-icon-48x48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zauberware/rails-devise-graphql/f3e71d39e9e5bb7d282af4546c65ccead6c67fb0/public/android-icon-48x48.png
--------------------------------------------------------------------------------
/public/android-icon-72x72.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zauberware/rails-devise-graphql/f3e71d39e9e5bb7d282af4546c65ccead6c67fb0/public/android-icon-72x72.png
--------------------------------------------------------------------------------
/public/android-icon-96x96.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zauberware/rails-devise-graphql/f3e71d39e9e5bb7d282af4546c65ccead6c67fb0/public/android-icon-96x96.png
--------------------------------------------------------------------------------
/public/apple-icon-114x114.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zauberware/rails-devise-graphql/f3e71d39e9e5bb7d282af4546c65ccead6c67fb0/public/apple-icon-114x114.png
--------------------------------------------------------------------------------
/public/apple-icon-120x120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zauberware/rails-devise-graphql/f3e71d39e9e5bb7d282af4546c65ccead6c67fb0/public/apple-icon-120x120.png
--------------------------------------------------------------------------------
/public/apple-icon-144x144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zauberware/rails-devise-graphql/f3e71d39e9e5bb7d282af4546c65ccead6c67fb0/public/apple-icon-144x144.png
--------------------------------------------------------------------------------
/public/apple-icon-152x152.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zauberware/rails-devise-graphql/f3e71d39e9e5bb7d282af4546c65ccead6c67fb0/public/apple-icon-152x152.png
--------------------------------------------------------------------------------
/public/apple-icon-180x180.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zauberware/rails-devise-graphql/f3e71d39e9e5bb7d282af4546c65ccead6c67fb0/public/apple-icon-180x180.png
--------------------------------------------------------------------------------
/public/apple-icon-57x57.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zauberware/rails-devise-graphql/f3e71d39e9e5bb7d282af4546c65ccead6c67fb0/public/apple-icon-57x57.png
--------------------------------------------------------------------------------
/public/apple-icon-60x60.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zauberware/rails-devise-graphql/f3e71d39e9e5bb7d282af4546c65ccead6c67fb0/public/apple-icon-60x60.png
--------------------------------------------------------------------------------
/public/apple-icon-72x72.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zauberware/rails-devise-graphql/f3e71d39e9e5bb7d282af4546c65ccead6c67fb0/public/apple-icon-72x72.png
--------------------------------------------------------------------------------
/public/apple-icon-76x76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zauberware/rails-devise-graphql/f3e71d39e9e5bb7d282af4546c65ccead6c67fb0/public/apple-icon-76x76.png
--------------------------------------------------------------------------------
/public/apple-icon-precomposed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zauberware/rails-devise-graphql/f3e71d39e9e5bb7d282af4546c65ccead6c67fb0/public/apple-icon-precomposed.png
--------------------------------------------------------------------------------
/public/apple-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zauberware/rails-devise-graphql/f3e71d39e9e5bb7d282af4546c65ccead6c67fb0/public/apple-icon.png
--------------------------------------------------------------------------------
/public/browserconfig.xml:
--------------------------------------------------------------------------------
1 |
2 | #ffffff
--------------------------------------------------------------------------------
/public/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zauberware/rails-devise-graphql/f3e71d39e9e5bb7d282af4546c65ccead6c67fb0/public/favicon-16x16.png
--------------------------------------------------------------------------------
/public/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zauberware/rails-devise-graphql/f3e71d39e9e5bb7d282af4546c65ccead6c67fb0/public/favicon-32x32.png
--------------------------------------------------------------------------------
/public/favicon-96x96.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zauberware/rails-devise-graphql/f3e71d39e9e5bb7d282af4546c65ccead6c67fb0/public/favicon-96x96.png
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zauberware/rails-devise-graphql/f3e71d39e9e5bb7d282af4546c65ccead6c67fb0/public/favicon.ico
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "App",
3 | "icons": [
4 | {
5 | "src": "\/android-icon-36x36.png",
6 | "sizes": "36x36",
7 | "type": "image\/png",
8 | "density": "0.75"
9 | },
10 | {
11 | "src": "\/android-icon-48x48.png",
12 | "sizes": "48x48",
13 | "type": "image\/png",
14 | "density": "1.0"
15 | },
16 | {
17 | "src": "\/android-icon-72x72.png",
18 | "sizes": "72x72",
19 | "type": "image\/png",
20 | "density": "1.5"
21 | },
22 | {
23 | "src": "\/android-icon-96x96.png",
24 | "sizes": "96x96",
25 | "type": "image\/png",
26 | "density": "2.0"
27 | },
28 | {
29 | "src": "\/android-icon-144x144.png",
30 | "sizes": "144x144",
31 | "type": "image\/png",
32 | "density": "3.0"
33 | },
34 | {
35 | "src": "\/android-icon-192x192.png",
36 | "sizes": "192x192",
37 | "type": "image\/png",
38 | "density": "4.0"
39 | }
40 | ]
41 | }
--------------------------------------------------------------------------------
/public/ms-icon-144x144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zauberware/rails-devise-graphql/f3e71d39e9e5bb7d282af4546c65ccead6c67fb0/public/ms-icon-144x144.png
--------------------------------------------------------------------------------
/public/ms-icon-150x150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zauberware/rails-devise-graphql/f3e71d39e9e5bb7d282af4546c65ccead6c67fb0/public/ms-icon-150x150.png
--------------------------------------------------------------------------------
/public/ms-icon-310x310.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zauberware/rails-devise-graphql/f3e71d39e9e5bb7d282af4546c65ccead6c67fb0/public/ms-icon-310x310.png
--------------------------------------------------------------------------------
/public/ms-icon-70x70.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zauberware/rails-devise-graphql/f3e71d39e9e5bb7d282af4546c65ccead6c67fb0/public/ms-icon-70x70.png
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file
2 |
--------------------------------------------------------------------------------
/spec/config/initializers/rack/attack_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'rails_helper'
4 |
5 | describe Rack::Attack do
6 | include Rack::Test::Methods
7 |
8 | def app
9 | Rails.application
10 | end
11 |
12 | before do
13 | described_class.cache.store = ActiveSupport::Cache::MemoryStore.new
14 | end
15 |
16 | (1..3).each do |level|
17 | describe "Throttle all requests by IP (Level #{level})" do
18 | let(:limit) { (ENV['ATTACK_REQUEST_LIMIT'] || 300).to_i * level }
19 | let(:period) { (ENV['ATTACK_REQUEST_PERIOD_IN_MINUTES'] || 5).to_i**level }
20 |
21 | context "when requests are lower than level #{level} limit" do
22 | it 'does not change the request status' do
23 | ip = Faker::Internet.ip_v4_address
24 | limit.times do |i|
25 | Timecop.freeze(DateTime.now + ((i + 1) * (60 / (limit / period.to_f))).seconds) do
26 | get '/', session: { 'REMOTE_ADDR' => ip }
27 | expect(last_response.status).not_to eq(429)
28 | end
29 | end
30 | end
31 | end
32 |
33 | context "when requests are higher than level #{level} limit" do
34 | it 'changes the request status to 429' do
35 | ip = Faker::Internet.ip_v4_address
36 | (limit * 2.0).to_i.times do |i|
37 | Timecop.freeze(DateTime.now + (i * (60 / ((limit * 2.0) / period.to_f))).seconds) do
38 | get '/', session: { 'REMOTE_ADDR' => ip }
39 | expect(last_response.status).to eq(429) if i >= limit * 2.0
40 | end
41 | end
42 | end
43 | end
44 | end
45 |
46 | describe "Throttle all authenticated requests by IP (Level #{level})" do
47 | let(:limit) { (ENV['ATTACK_AUTHENTICATED_REQUEST_LIMIT'] || 300).to_i * level }
48 | let(:period) { (ENV['ATTACK_AUTHENTICATED_REQUEST_PERIOD_IN_MINUTES'] || 5).to_i**level }
49 | let(:user) { create(:user) }
50 |
51 | before { allow(described_class).to receive(:user_session?) { user } }
52 |
53 | context "when requests are lower than level #{level} limit" do
54 | it 'does not change the request status' do
55 | ip = Faker::Internet.ip_v4_address
56 | limit.times do |i|
57 | Timecop.freeze(DateTime.now + ((i + 1) * (60 / (limit / period.to_f))).seconds) do
58 | get '/', session: { 'REMOTE_ADDR' => ip }
59 | expect(last_response.status).not_to eq(429)
60 | end
61 | end
62 | end
63 | end
64 |
65 | context "when requests are higher than level #{level} limit" do
66 | it 'changes the request status to 429' do
67 | ip = Faker::Internet.ip_v4_address
68 | (limit * 2.0).to_i.times do |i|
69 | Timecop.freeze(DateTime.now + (i * (60 / ((limit * 2.0) / period.to_f))).seconds) do
70 | get '/', session: { 'REMOTE_ADDR' => ip }
71 | expect(last_response.status).to eq(429) if i >= limit * 2.0
72 | end
73 | end
74 | end
75 | end
76 | end
77 |
78 | describe "Throttle devise sign in requests (Level #{level})" do
79 | let(:limit) { (ENV['ATTACK_AUTH_LIMIT'] || 10).to_i * level }
80 | let(:period) { (ENV['ATTACK_AUTH_PERIOD_IN_MINUTES'] || 10).to_i**level }
81 | let(:user) { create(:user) }
82 |
83 | before { allow(described_class).to receive(:user_session?) { user } }
84 |
85 | context "when requests are lower than level #{level} limit" do
86 | it 'does not change the request status' do
87 | ip = Faker::Internet.ip_v4_address
88 | limit.times do |i|
89 | Timecop.freeze(DateTime.now + ((i + 1) * (60 / (limit / period.to_f))).seconds) do
90 | post '/users/sign_in', params: { user: { email: user.email, password: 'wrong' } }, 'REMOTE_ADDR' => ip
91 | expect(last_response.status).not_to eq(429)
92 | end
93 | end
94 | end
95 | end
96 |
97 | context "when requests are higher than level #{level} limit" do
98 | it 'changes the request status to 429' do
99 | ip = Faker::Internet.ip_v4_address
100 | (limit * 2.0).to_i.times do |i|
101 | Timecop.freeze(DateTime.now + (i * (60 / ((limit * 2.0) / period.to_f))).seconds) do
102 | post '/users/sign_in', params: { user: { email: user.email, password: 'wrong' } }, 'REMOTE_ADDR' => ip
103 | expect(last_response.status).to eq(429) if i >= limit * 2.0
104 | end
105 | end
106 | end
107 | end
108 | end
109 |
110 | describe "Throttle devise password requests (Level #{level})" do
111 | let(:limit) { (ENV['ATTACK_AUTH_LIMIT'] || 10).to_i * level }
112 | let(:period) { (ENV['ATTACK_AUTH_PERIOD_IN_MINUTES'] || 10).to_i**level }
113 | let(:user) { create(:user) }
114 |
115 | before { allow(described_class).to receive(:user_session?) { user } }
116 |
117 | context "when requests are lower than level #{level} limit" do
118 | it 'does not change the request status' do
119 | ip = Faker::Internet.ip_v4_address
120 | limit.times do |i|
121 | Timecop.freeze(DateTime.now + ((i + 1) * (60 / (limit / period.to_f))).seconds) do
122 | post '/users/password', params: { user: { email: user.email } }, 'REMOTE_ADDR' => ip
123 | expect(last_response.status).not_to eq(429)
124 | end
125 | end
126 | end
127 | end
128 |
129 | context "when requests are higher than level #{level} limit" do
130 | it 'changes the request status to 429' do
131 | ip = Faker::Internet.ip_v4_address
132 | (limit * 2.0).to_i.times do |i|
133 | Timecop.freeze(DateTime.now + (i * (60 / ((limit * 2.0) / period.to_f))).seconds) do
134 | post '/users/password', params: { user: { email: user.email } }, 'REMOTE_ADDR' => ip
135 | expect(last_response.status).to eq(429) if i >= limit * 2.0
136 | end
137 | end
138 | end
139 | end
140 | end
141 |
142 | describe "Throttle graphQL auth requests (Level #{level})" do
143 | let(:limit) { (ENV['ATTACK_AUTH_LIMIT'] || 10).to_i * level }
144 | let(:period) { (ENV['ATTACK_AUTH_PERIOD_IN_MINUTES'] || 10).to_i**level }
145 | let(:user) { create(:user) }
146 |
147 | before { allow(described_class).to receive(:user_session?) { user } }
148 |
149 | context "when requests are lower than level #{level} limit" do
150 | it 'does not change the request status' do
151 | ip = Faker::Internet.ip_v4_address
152 | limit.times do |i|
153 | Timecop.freeze(DateTime.now + ((i + 1) * (60 / (limit / period.to_f))).seconds) do
154 | post '/graphql', params: { query: 'signUp' }, 'REMOTE_ADDR' => ip
155 | expect(last_response.status).not_to eq(429)
156 | end
157 | end
158 | end
159 | end
160 |
161 | context "when requests are higher than level #{level} limit" do
162 | it 'changes the request status to 429' do
163 | ip = Faker::Internet.ip_v4_address
164 | (limit * 2.0).to_i.times do |i|
165 | Timecop.freeze(DateTime.now + (i * (60 / ((limit * 2.0) / period.to_f))).seconds) do
166 | post '/graphql', params: { query: 'signUp' }, 'REMOTE_ADDR' => ip
167 | expect(last_response.status).to eq(429) if i >= limit * 2.0
168 | end
169 | end
170 | end
171 | end
172 | end
173 | end
174 |
175 | describe 'Throttle graphQL actions' do
176 | let(:limit) { (ENV['ATTACK_PUBLIC_ACTION_LIMIT'] || 30).to_i }
177 | let(:period) { (ENV['ATTACK_PUBLIC_ACTION_PERIOD_IN_MINUTES'] || 60).to_i }
178 | let(:user) { create(:user) }
179 |
180 | before { allow(described_class).to receive(:user_session?) { user } }
181 |
182 | context 'when requests are lower than limit' do
183 | it 'does not change the request status' do
184 | ip = Faker::Internet.ip_v4_address
185 | limit.times do |i|
186 | Timecop.freeze(DateTime.now + ((i + 1) * (60 / (limit / period.to_f))).seconds) do
187 | post '/graphql', params: { query: 'createMessage' }, 'REMOTE_ADDR' => ip
188 | expect(last_response.status).not_to eq(429)
189 | end
190 | end
191 | end
192 | end
193 |
194 | context 'when requests are higher than limit' do
195 | it 'changes the request status to 429' do
196 | ip = Faker::Internet.ip_v4_address
197 | (limit * 2.0).to_i.times do |i|
198 | Timecop.freeze(DateTime.now + (i * (60 / ((limit * 2.0) / period.to_f))).seconds) do
199 | post '/graphql', params: { query: 'createMessage' }, 'REMOTE_ADDR' => ip
200 | expect(last_response.status).to eq(429) if i >= limit * 2.0
201 | end
202 | end
203 | end
204 | end
205 | end
206 | end
207 |
--------------------------------------------------------------------------------
/spec/controllers/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zauberware/rails-devise-graphql/f3e71d39e9e5bb7d282af4546c65ccead6c67fb0/spec/controllers/.keep
--------------------------------------------------------------------------------
/spec/controllers/graphql_controller_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'rails_helper'
4 |
5 | RSpec.describe GraphqlController, type: :controller do
6 | login_user # access current user with @current_user
7 |
8 | describe 'execute' do
9 | let(:current_user) { @current_user }
10 |
11 | context 'when wrong query params given' do
12 | it 'returns with errors' do
13 | post :execute, params: { 'query' => "{\n wrong {\n email\n }\n}" }
14 | response_body = JSON.parse(response.body)
15 | expect(response_body['errors']).not_to be nil
16 | end
17 | end
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/spec/factories/companies/companies.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # == Schema Information
4 | #
5 | # Table name: companies
6 | #
7 | # id :uuid not null, primary key
8 | # name :string
9 | # slug :string
10 | # users_count :integer
11 | # created_at :datetime not null
12 | # updated_at :datetime not null
13 | #
14 | # Indexes
15 | #
16 | # index_companies_on_slug (slug) UNIQUE
17 | #
18 | FactoryBot.define do
19 | factory :company, class: 'Companies::Company' do
20 | name { 'My Company' }
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/spec/factories/users.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # == Schema Information
4 | #
5 | # Table name: users
6 | #
7 | # id :uuid not null, primary key
8 | # confirmation_sent_at :datetime
9 | # confirmation_token :string
10 | # confirmed_at :datetime
11 | # current_sign_in_at :datetime
12 | # current_sign_in_ip :inet
13 | # email :string default(""), not null
14 | # encrypted_password :string default(""), not null
15 | # failed_attempts :integer default(0), not null
16 | # first_name :string default(""), not null
17 | # invitation_accepted_at :datetime
18 | # invitation_created_at :datetime
19 | # invitation_limit :integer
20 | # invitation_sent_at :datetime
21 | # invitation_token :string
22 | # invitations_count :integer default(0)
23 | # invited_by_type :string
24 | # last_name :string default(""), not null
25 | # last_seen_at :datetime
26 | # last_sign_in_at :datetime
27 | # last_sign_in_ip :inet
28 | # locked_at :datetime
29 | # refresh_token :string
30 | # remember_created_at :datetime
31 | # reset_password_sent_at :datetime
32 | # reset_password_token :string
33 | # role :integer default("user"), not null
34 | # sign_in_count :integer default(0), not null
35 | # unconfirmed_email :string
36 | # unlock_token :string
37 | # created_at :datetime not null
38 | # updated_at :datetime not null
39 | # company_id :uuid
40 | # invited_by_id :bigint
41 | #
42 | # Indexes
43 | #
44 | # index_users_on_confirmation_token (confirmation_token) UNIQUE
45 | # index_users_on_email (email) UNIQUE
46 | # index_users_on_invitation_token (invitation_token) UNIQUE
47 | # index_users_on_invitations_count (invitations_count)
48 | # index_users_on_invited_by_id (invited_by_id)
49 | # index_users_on_invited_by_type_and_invited_by_id (invited_by_type,invited_by_id)
50 | # index_users_on_refresh_token (refresh_token) UNIQUE
51 | # index_users_on_reset_password_token (reset_password_token) UNIQUE
52 | # index_users_on_unlock_token (unlock_token) UNIQUE
53 | #
54 | FactoryBot.define do
55 | factory :user do
56 | first_name { Faker::Name.first_name }
57 | last_name { Faker::Name.last_name }
58 | email { Faker::Internet.email }
59 | password { 'password' }
60 | password_confirmation { 'password' }
61 | association :company
62 |
63 | trait :user do
64 | role { :user }
65 | end
66 |
67 | trait :admin do
68 | role { :admin }
69 | end
70 | end
71 | end
72 |
--------------------------------------------------------------------------------
/spec/fixtures/files/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zauberware/rails-devise-graphql/f3e71d39e9e5bb7d282af4546c65ccead6c67fb0/spec/fixtures/files/.keep
--------------------------------------------------------------------------------
/spec/graphql/mutations/companies/update_company_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'rails_helper'
4 |
5 | RSpec.describe Mutations::Companies::UpdateCompany do
6 | subject(:graphql!) { result }
7 |
8 | let(:result) do
9 | GraphqlSchema.execute(
10 | query_string,
11 | variables: variables,
12 | context: context
13 | )
14 | end
15 |
16 | let(:variables) do
17 | {}
18 | end
19 |
20 | let(:query_string) do
21 | <<-GRAPHQL
22 | mutation updateCompany($id: ID!, $attributes: CompanyInput!){
23 | updateCompany(id: $id, attributes: $attributes) {
24 | id
25 | name
26 | }
27 | }
28 | GRAPHQL
29 | end
30 |
31 | describe 'updateCompany' do
32 | context 'with invalid company id' do
33 | let!(:user) do
34 | create(:user)
35 | end
36 |
37 | let(:context) do
38 | {
39 | current_user: user
40 | }
41 | end
42 |
43 | let(:variables) do
44 | {
45 | id: 'wrong',
46 | attributes: {}
47 | }
48 | end
49 |
50 | it 'returns errors' do
51 | message = result['errors'][0]['message']
52 | expect(message).not_to be_nil
53 | end
54 | end
55 |
56 | context 'with invalid params' do
57 | let!(:user) do
58 | create(:user)
59 | end
60 |
61 | let(:context) do
62 | {
63 | current_user: user
64 | }
65 | end
66 |
67 | let(:variables) do
68 | {
69 | id: user.company_id,
70 | attributes: {}
71 | }
72 | end
73 |
74 | it 'returns errors' do
75 | graphql!
76 | message = result['errors'][0]['message']
77 | expect(message).not_to be_nil
78 | end
79 | end
80 |
81 | context 'with valid params' do
82 | let!(:user) do
83 | create(:user, :admin)
84 | end
85 |
86 | let(:context) do
87 | {
88 | current_user: user
89 | }
90 | end
91 |
92 | let(:variables) do
93 | {
94 | id: user.company_id,
95 | attributes: { name: 'new name' }
96 | }
97 | end
98 |
99 | it 'changes name' do
100 | graphql!
101 | name = result['data']['updateCompany']['name']
102 | expect(name).to eq('new name')
103 | end
104 | end
105 | end
106 | end
107 |
--------------------------------------------------------------------------------
/spec/graphql/mutations/users/delete_user_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'rails_helper'
4 |
5 | RSpec.describe Mutations::Users::DeleteUser do
6 | subject(:graphql!) { result }
7 |
8 | let!(:admin) do
9 | create(:user, :admin)
10 | end
11 |
12 | let(:result) do
13 | GraphqlSchema.execute(
14 | query_string,
15 | variables: variables,
16 | context: context
17 | )
18 | end
19 |
20 | let(:variables) do
21 | {}
22 | end
23 |
24 | let(:query_string) do
25 | <<-GRAPHQL
26 | mutation deleteUser($id: ID!){
27 | deleteUser(id: $id)
28 | }
29 | GRAPHQL
30 | end
31 |
32 | describe 'deleteUser' do
33 | context 'when not an admin' do
34 | let(:user) do
35 | create(:user, company_id: admin.company_id)
36 | end
37 |
38 | let(:context) do
39 | {
40 | current_user: user
41 | }
42 | end
43 |
44 | let(:variables) do
45 | {
46 | id: user.id
47 | }
48 | end
49 |
50 | it 'returns errors' do
51 | graphql!
52 | message = result['errors'][0]['message']
53 | expect(message).not_to be_nil
54 | end
55 | end
56 |
57 | context 'with invalid id' do
58 | let(:user) do
59 | create(:user, company_id: admin.company_id)
60 | end
61 |
62 | let(:context) do
63 | {
64 | current_user: admin
65 | }
66 | end
67 |
68 | let(:variables) do
69 | {
70 | id: 'wrong'
71 | }
72 | end
73 |
74 | it 'returns nil' do
75 | graphql!
76 | success = result['data']['deleteUser']
77 | expect(success).to be_nil
78 | end
79 | end
80 |
81 | context 'with valid params' do
82 | let!(:user) do
83 | create(:user, company_id: admin.company_id)
84 | end
85 |
86 | let(:context) do
87 | {
88 | current_user: admin
89 | }
90 | end
91 |
92 | let(:variables) do
93 | {
94 | id: user.id
95 | }
96 | end
97 |
98 | it 'changes name' do
99 | graphql!
100 | success = result['data']['deleteUser']
101 | expect(success).to eq(true)
102 | end
103 | end
104 | end
105 | end
106 |
--------------------------------------------------------------------------------
/spec/graphql/mutations/users/update_user_role_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'rails_helper'
4 |
5 | RSpec.describe Mutations::Users::UpdateUserRole do
6 | subject(:graphql!) { result }
7 |
8 | let!(:admin) do
9 | create(:user, :admin)
10 | end
11 |
12 | let(:result) do
13 | GraphqlSchema.execute(
14 | query_string,
15 | variables: variables,
16 | context: context
17 | )
18 | end
19 |
20 | let(:variables) do
21 | {}
22 | end
23 |
24 | let(:query_string) do
25 | <<-GRAPHQL
26 | mutation updateUserRole($id: ID!, $role: String!){
27 | updateUserRole(id: $id, role: $role)
28 | }
29 | GRAPHQL
30 | end
31 |
32 | describe 'updateUser' do
33 | context 'when not an admin' do
34 | let(:user) do
35 | create(:user, company_id: admin.company_id)
36 | end
37 |
38 | let(:context) do
39 | {
40 | current_user: user
41 | }
42 | end
43 |
44 | let(:variables) do
45 | {
46 | id: user.id,
47 | role: 'admin'
48 | }
49 | end
50 |
51 | it 'returns errors' do
52 | graphql!
53 | message = result['errors'][0]['message']
54 | expect(message).not_to be_nil
55 | end
56 |
57 | it 'not updates user role' do
58 | graphql!
59 | expect(user.role).to eq('user')
60 | end
61 | end
62 |
63 | context 'with invalid id' do
64 | let(:user) do
65 | create(:user, company_id: admin.company_id)
66 | end
67 |
68 | let(:context) do
69 | {
70 | current_user: admin
71 | }
72 | end
73 |
74 | let(:variables) do
75 | {
76 | id: 'wrong',
77 | role: 'admin'
78 | }
79 | end
80 |
81 | it 'returns errors' do
82 | graphql!
83 | message = result['data']['updateUserRolw']
84 | expect(message).to be_nil
85 | end
86 | end
87 |
88 | context 'with invalid params' do
89 | let(:user) do
90 | create(:user, company_id: admin.company_id)
91 | end
92 |
93 | let(:context) do
94 | {
95 | current_user: admin
96 | }
97 | end
98 |
99 | let(:variables) do
100 | {
101 | id: user.id,
102 | role: 'superadmin'
103 | }
104 | end
105 |
106 | it 'returns false' do
107 | graphql!
108 | success = result['data']['updateUserRole']
109 | expect(success).to eq(false)
110 | end
111 |
112 | it 'not updates user role' do
113 | graphql!
114 | expect(user.role).to eq('user')
115 | end
116 | end
117 |
118 | context 'with valid params' do
119 | let!(:user) do
120 | create(:user, company_id: admin.company_id)
121 | end
122 |
123 | let(:context) do
124 | {
125 | current_user: admin
126 | }
127 | end
128 |
129 | let(:variables) do
130 | {
131 | id: user.id,
132 | role: 'admin'
133 | }
134 | end
135 |
136 | it 'returns true' do
137 | graphql!
138 | success = result['data']['updateUserRole']
139 | expect(success).to eq(true)
140 | end
141 |
142 | it 'updates user role' do
143 | graphql!
144 | expect(user.reload.role).to eq('admin')
145 | end
146 | end
147 | end
148 | end
149 |
--------------------------------------------------------------------------------
/spec/graphql/mutations/users/update_user_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'rails_helper'
4 |
5 | RSpec.describe Mutations::Users::UpdateUser do
6 | subject(:graphql!) { result }
7 |
8 | let!(:admin) do
9 | create(:user, :admin)
10 | end
11 |
12 | let(:result) do
13 | GraphqlSchema.execute(
14 | query_string,
15 | variables: variables,
16 | context: context
17 | )
18 | end
19 |
20 | let(:variables) do
21 | {}
22 | end
23 |
24 | let(:query_string) do
25 | <<-GRAPHQL
26 | mutation updateUser($id: ID!, $attributes: UserInput!){
27 | updateUser(id: $id, attributes: $attributes) {
28 | id
29 | firstName
30 | lastName
31 | email
32 | }
33 | }
34 | GRAPHQL
35 | end
36 |
37 | describe 'updateUser' do
38 | context 'when not an admin' do
39 | let(:user) do
40 | create(:user, company_id: admin.company_id)
41 | end
42 |
43 | let(:context) do
44 | {
45 | current_user: user
46 | }
47 | end
48 |
49 | let(:variables) do
50 | {
51 | id: user.id,
52 | attributes: {
53 | email: 'mail@pete.de',
54 | lastName: 'new last name',
55 | firstName: 'new first name'
56 | }
57 | }
58 | end
59 |
60 | it 'returns errors' do
61 | graphql!
62 | message = result['errors'][0]['message']
63 | expect(message).not_to be_nil
64 | end
65 | end
66 |
67 | context 'with invalid id' do
68 | let(:user) do
69 | create(:user, company_id: admin.company_id)
70 | end
71 |
72 | let(:context) do
73 | {
74 | current_user: admin
75 | }
76 | end
77 |
78 | let(:variables) do
79 | {
80 | id: 'wrong',
81 | attributes: {}
82 | }
83 | end
84 |
85 | it 'returns errors' do
86 | graphql!
87 | message = result['errors'][0]['message']
88 | expect(message).not_to be_nil
89 | end
90 | end
91 |
92 | context 'with invalid params' do
93 | let(:user) do
94 | create(:user, company_id: admin.company_id)
95 | end
96 |
97 | let(:context) do
98 | {
99 | current_user: admin
100 | }
101 | end
102 |
103 | let(:variables) do
104 | {
105 | id: user.id,
106 | attributes: {}
107 | }
108 | end
109 |
110 | it 'returns errors' do
111 | graphql!
112 | message = result['errors'][0]['message']
113 | expect(message).not_to be_nil
114 | end
115 | end
116 |
117 | context 'with valid params' do
118 | let!(:user) do
119 | create(:user, company_id: admin.company_id)
120 | end
121 |
122 | let(:context) do
123 | {
124 | current_user: admin
125 | }
126 | end
127 |
128 | let(:variables) do
129 | {
130 | id: user.id,
131 | attributes: {
132 | email: 'mail@pete.de',
133 | lastName: 'new last name',
134 | firstName: 'new first name'
135 | }
136 | }
137 | end
138 |
139 | it 'changes name' do
140 | graphql!
141 | name = result['data']['updateUser']['firstName']
142 | expect(name).to eq('new first name')
143 | end
144 | end
145 | end
146 | end
147 |
--------------------------------------------------------------------------------
/spec/graphql/resolvers/companies/company_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'rails_helper'
4 |
5 | RSpec.describe Resolvers::Companies::Company, type: :request do
6 | subject(:graphql!) { result }
7 |
8 | let(:result) do
9 | GraphqlSchema.execute(
10 | query_string,
11 | variables: variables,
12 | context: context
13 | )
14 | end
15 |
16 | let(:variables) do
17 | {}
18 | end
19 |
20 | let(:query_string) do
21 | <<-GRAPHQL
22 | query {
23 | company {
24 | id
25 | name
26 | }
27 | }
28 | GRAPHQL
29 | end
30 |
31 | describe 'company' do
32 | context 'when there\'s no current user' do
33 | let(:context) do
34 | {
35 | current_user: nil
36 | }
37 | end
38 |
39 | it 'returns nil' do
40 | graphql!
41 | expect(result['data']['company']).to eq(nil)
42 | end
43 | end
44 |
45 | context 'when there\'s a current user' do
46 | let!(:user) do
47 | create(:user)
48 | end
49 |
50 | let(:context) do
51 | {
52 | current_user: user
53 | }
54 | end
55 |
56 | it 'returns company of user' do
57 | graphql!
58 | company_id = result['data']['company']['id']
59 | expect(company_id).to eq(user.company.id)
60 | end
61 | end
62 | end
63 | end
64 |
--------------------------------------------------------------------------------
/spec/graphql/resolvers/users/me_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'rails_helper'
4 |
5 | RSpec.describe Resolvers::Users::Me, type: :request do
6 | subject(:graphql!) { result }
7 |
8 | let(:result) do
9 | GraphqlSchema.execute(
10 | query_string,
11 | variables: variables,
12 | context: context
13 | )
14 | end
15 |
16 | let(:variables) do
17 | {}
18 | end
19 |
20 | let(:query_string) do
21 | <<-GRAPHQL
22 | query {
23 | me {
24 | name
25 | isConfirmed
26 | isLocked
27 | }
28 | }
29 | GRAPHQL
30 | end
31 |
32 | describe 'me' do
33 | context 'when there\'s no current user' do
34 | let(:context) do
35 | {
36 | current_user: nil
37 | }
38 | end
39 |
40 | it 'is nil' do
41 | graphql!
42 | expect(result['data']['me']).to eq(nil)
43 | end
44 | end
45 |
46 | context 'when there\'s a current user' do
47 | let!(:user) do
48 | create(
49 | :user,
50 | first_name: 'A',
51 | last_name: 'B'
52 | )
53 | end
54 |
55 | let(:context) do
56 | {
57 | current_user: user
58 | }
59 | end
60 |
61 | it 'shows the user\'s name' do
62 | graphql!
63 | user_name = result['data']['me']['name']
64 | expect(user_name).to eq('A B')
65 | end
66 | end
67 | end
68 | end
69 |
--------------------------------------------------------------------------------
/spec/graphql/resolvers/users/user_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'rails_helper'
4 |
5 | RSpec.describe Resolvers::Users::User, type: :request do
6 | subject(:graphql!) { result }
7 |
8 | let!(:company) do
9 | create(:company)
10 | end
11 |
12 | let!(:admin) do
13 | create(:user, :admin, company_id: company.id)
14 | end
15 |
16 | let(:result) do
17 | GraphqlSchema.execute(
18 | query_string,
19 | variables: variables,
20 | context: context
21 | )
22 | end
23 |
24 | let(:query_string) do
25 | <<-GRAPHQL
26 | query($id: ID!) {
27 | user(id: $id){
28 | id
29 | email
30 | firstName
31 | lastName
32 | }
33 | }
34 | GRAPHQL
35 | end
36 |
37 | let(:context) do
38 | { current_user: admin }
39 | end
40 |
41 | describe 'user' do
42 | context 'when user is not member of this company' do
43 | let(:user) do
44 | create(:user, company: create(:company))
45 | end
46 |
47 | let(:variables) do
48 | { 'id' => user.id }
49 | end
50 |
51 | it 'returns' do
52 | graphql!
53 | expect(result['data']['user']).to eq(nil)
54 | end
55 | end
56 |
57 | context 'when user is admin of this company' do
58 | let(:user) do
59 | create(:user, company_id: company.id)
60 | end
61 |
62 | let(:variables) do
63 | { 'id' => user.id }
64 | end
65 |
66 | it 'can access users profile' do
67 | graphql!
68 | user_email = result['data']['user']['email']
69 | expect(user_email).to eq(user.email)
70 | end
71 | end
72 |
73 | context 'when user is member of this company' do
74 | let(:user) do
75 | create(:user, company_id: company.id)
76 | end
77 |
78 | let(:user2) do
79 | create(:user, company_id: company.id)
80 | end
81 |
82 | let(:context) do
83 | { current_user: user }
84 | end
85 |
86 | let(:variables) do
87 | { 'id' => user2.id }
88 | end
89 |
90 | it 'can access users profile' do
91 | graphql!
92 | user_email = result['data']['user']['email']
93 | expect(user_email).to eq(user2.email)
94 | end
95 | end
96 | end
97 | end
98 |
--------------------------------------------------------------------------------
/spec/graphql/resolvers/users/users_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'rails_helper'
4 |
5 | RSpec.describe Resolvers::Users::Users, type: :request do
6 | subject(:graphql!) { result }
7 |
8 | let!(:company) do
9 | create(:company)
10 | end
11 |
12 | let!(:admin) do
13 | create(:user, :admin, company_id: company.id)
14 | end
15 |
16 | let(:result) do
17 | GraphqlSchema.execute(
18 | query_string,
19 | variables: variables,
20 | context: context
21 | )
22 | end
23 |
24 | let(:query_string) do
25 | <<-GRAPHQL
26 | query($orderBy: ItemOrder, $filter: UserFilter, $after: String, $before: String, $first: Int, $last: Int) {
27 | users(orderBy: $orderBy, filter: $filter, after: $after, before: $before, first: $first, last: $last){
28 | pageInfo {
29 | endCursor
30 | startCursor
31 | hasPreviousPage
32 | hasNextPage
33 | }
34 | edges{
35 | node{
36 | id
37 | email
38 | firstName
39 | lastName
40 | }
41 | }
42 | }
43 | }
44 | GRAPHQL
45 | end
46 |
47 | let(:context) do
48 | { current_user: admin }
49 | end
50 |
51 | let(:variables) { {} }
52 |
53 | describe 'users' do
54 | context 'when there\'s no current user' do
55 | let(:context) { { current_user: nil } }
56 |
57 | before { create_list(:user, 3) }
58 |
59 | it 'returns empty Array' do
60 | graphql!
61 | expect(result['data']['users']['edges']).to be_empty
62 | end
63 | end
64 |
65 | context 'when there\'s a current user' do
66 | let(:user) { create(:user) }
67 |
68 | let(:context) { { current_user: user } }
69 |
70 | before { create_list(:user, 3, company: user.company) }
71 |
72 | it 'returns user in edges.' do
73 | graphql!
74 | expect(result['data']['users']['edges'].length).to eq(4)
75 | end
76 |
77 | it 'returns pageInfo' do
78 | graphql!
79 | expect(result['data']['users']['pageInfo']['startCursor']).not_to be_empty
80 | end
81 | end
82 |
83 | context 'when filters set' do
84 | let(:user) { create(:user) }
85 | let(:user2) { create(:user) }
86 |
87 | let(:context) { { current_user: user } }
88 |
89 | let(:variables) do
90 | {
91 | filter: {
92 | firstName: user.first_name
93 | }
94 | }
95 | end
96 |
97 | before { create_list(:user, 3, company: user.company) }
98 |
99 | it 'returns only filtered user' do
100 | graphql!
101 | users = result['data']['users']['edges']
102 | expect(users.first['node']['id']).to eq(user.id)
103 | end
104 |
105 | it 'returns only the filterd one' do
106 | graphql!
107 | users = result['data']['users']['edges']
108 | expect(users.length).to eq(1)
109 | end
110 |
111 | it 'returns pageInfo' do
112 | graphql!
113 | expect(result['data']['users']['pageInfo']['startCursor']).not_to be_empty
114 | end
115 |
116 | it 'not has errors' do
117 | graphql!
118 | expect(result['errors']).to be_nil
119 | end
120 | end
121 |
122 | context 'when orderBy set' do
123 | let(:user) { create(:user) }
124 | let(:user2) { create(:user) }
125 |
126 | let(:context) { { current_user: user } }
127 |
128 | let(:variables) do
129 | {
130 | orderBy: {
131 | attribute: 'first_name',
132 | direction: 'asc'
133 | }
134 | }
135 | end
136 |
137 | before { create_list(:user, 20, company: user.company) }
138 |
139 | it 'returns ordered users' do
140 | graphql!
141 | users = result['data']['users']['edges']
142 | (users.length - 2).times do |i|
143 | expect(users[i]['node']['firstName'][0] <= users[i + 1]['node']['firstName'][0]).to be_truthy
144 | end
145 | end
146 |
147 | it 'returns pageInfo' do
148 | graphql!
149 | expect(result['data']['users']['pageInfo']['startCursor']).not_to be_empty
150 | end
151 |
152 | it 'not has errors' do
153 | graphql!
154 | expect(result['errors']).to be_nil
155 | end
156 | end
157 | end
158 | end
159 |
--------------------------------------------------------------------------------
/spec/graphql/types/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zauberware/rails-devise-graphql/f3e71d39e9e5bb7d282af4546c65ccead6c67fb0/spec/graphql/types/.keep
--------------------------------------------------------------------------------
/spec/locales/locale_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # testing all locale files
4 | Dir.glob('config/locales/**/*.yml') do |locale_file|
5 | RSpec.describe "Locale file #{locale_file}" do
6 | it_behaves_like 'a valid locale file', locale_file
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/spec/mailers/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zauberware/rails-devise-graphql/f3e71d39e9e5bb7d282af4546c65ccead6c67fb0/spec/mailers/.keep
--------------------------------------------------------------------------------
/spec/models/companies/company_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # == Schema Information
4 | #
5 | # Table name: companies
6 | #
7 | # id :uuid not null, primary key
8 | # name :string
9 | # slug :string
10 | # users_count :integer
11 | # created_at :datetime not null
12 | # updated_at :datetime not null
13 | #
14 | # Indexes
15 | #
16 | # index_companies_on_slug (slug) UNIQUE
17 | #
18 | require 'rails_helper'
19 |
20 | RSpec.describe Companies::Company, type: :model do
21 | it 'has a valid factory' do
22 | expect(create(:company)).to be_valid
23 | end
24 |
25 | # Validations
26 | it { is_expected.to validate_presence_of(:name) }
27 | it { is_expected.to validate_length_of(:name).is_at_most(255) }
28 | end
29 |
--------------------------------------------------------------------------------
/spec/models/user_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # == Schema Information
4 | #
5 | # Table name: users
6 | #
7 | # id :uuid not null, primary key
8 | # confirmation_sent_at :datetime
9 | # confirmation_token :string
10 | # confirmed_at :datetime
11 | # current_sign_in_at :datetime
12 | # current_sign_in_ip :inet
13 | # email :string default(""), not null
14 | # encrypted_password :string default(""), not null
15 | # failed_attempts :integer default(0), not null
16 | # first_name :string default(""), not null
17 | # invitation_accepted_at :datetime
18 | # invitation_created_at :datetime
19 | # invitation_limit :integer
20 | # invitation_sent_at :datetime
21 | # invitation_token :string
22 | # invitations_count :integer default(0)
23 | # invited_by_type :string
24 | # last_name :string default(""), not null
25 | # last_seen_at :datetime
26 | # last_sign_in_at :datetime
27 | # last_sign_in_ip :inet
28 | # locked_at :datetime
29 | # refresh_token :string
30 | # remember_created_at :datetime
31 | # reset_password_sent_at :datetime
32 | # reset_password_token :string
33 | # role :integer default("user"), not null
34 | # sign_in_count :integer default(0), not null
35 | # unconfirmed_email :string
36 | # unlock_token :string
37 | # created_at :datetime not null
38 | # updated_at :datetime not null
39 | # company_id :uuid
40 | # invited_by_id :bigint
41 | #
42 | # Indexes
43 | #
44 | # index_users_on_confirmation_token (confirmation_token) UNIQUE
45 | # index_users_on_email (email) UNIQUE
46 | # index_users_on_invitation_token (invitation_token) UNIQUE
47 | # index_users_on_invitations_count (invitations_count)
48 | # index_users_on_invited_by_id (invited_by_id)
49 | # index_users_on_invited_by_type_and_invited_by_id (invited_by_type,invited_by_id)
50 | # index_users_on_refresh_token (refresh_token) UNIQUE
51 | # index_users_on_reset_password_token (reset_password_token) UNIQUE
52 | # index_users_on_unlock_token (unlock_token) UNIQUE
53 | #
54 | require 'rails_helper'
55 |
56 | RSpec.describe User, type: :model do
57 | it 'has a valid factory' do
58 | expect(create(:user)).to be_valid
59 | expect(create(:user, :user)).to be_valid
60 | expect(create(:user, :admin)).to be_valid
61 | end
62 |
63 | # Validations
64 | it { is_expected.to validate_length_of(:first_name).is_at_most(255) }
65 | it { is_expected.to validate_length_of(:last_name).is_at_most(255) }
66 | it { is_expected.to validate_presence_of(:email) }
67 | it { is_expected.to validate_length_of(:email).is_at_most(255) }
68 | it { is_expected.to allow_value('email@address.foo').for(:email) }
69 | it { is_expected.not_to allow_value('email').for(:email) }
70 | it { is_expected.not_to allow_value('email@domain').for(:email) }
71 | it { is_expected.not_to allow_value('email@domain.').for(:email) }
72 | it { is_expected.not_to allow_value('email@domain.a').for(:email) }
73 |
74 | # Callbacks
75 | describe '#setup_new_user' do
76 | let(:user) { build(:user) }
77 |
78 | it 'sets role to user' do
79 | expect(user.role).to eq 'user'
80 | end
81 | end
82 |
83 | describe '#setup_company' do
84 | context 'when company is not set' do
85 | let(:user) { create(:user, company: nil) }
86 |
87 | it 'creates a new company' do
88 | expect(user.company).not_to be_nil
89 | end
90 |
91 | it 'makes this user an admin' do
92 | expect(user.role).to eq 'admin'
93 | end
94 | end
95 |
96 | context 'when company is set' do
97 | let(:user) { create(:user) }
98 |
99 | it 'has a company' do
100 | expect(user.company).not_to be_nil
101 | end
102 |
103 | it 'gives user role' do
104 | expect(user.role).to eq 'user'
105 | end
106 | end
107 | end
108 |
109 | # Methods
110 | describe '#name' do
111 | it 'returns first and lastname' do
112 | expect(create(:user, first_name: 'A', last_name: 'B').name).to eq 'A B'
113 | end
114 | end
115 | end
116 |
--------------------------------------------------------------------------------
/spec/rails_helper.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | ENV["RAILS_ENV"] = "test"
4 | require "simplecov"
5 | SimpleCov.start "rails"
6 | require "spec_helper"
7 | require File.expand_path('../config/environment', __dir__)
8 | # Prevent database truncation if the environment is production
9 | abort("The Rails environment is running in production mode!") if Rails.env.production?
10 | require "rspec/rails"
11 | require "shoulda/matchers"
12 |
13 | # Add additional requires below this line. Rails is not loaded until this point!
14 |
15 | # Requires supporting ruby files with custom matchers and macros, etc, in
16 | # spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are
17 | # run as spec files by default. This means that files in spec/support that end
18 | # in _spec.rb will both be required and run as specs, causing the specs to be
19 | # run twice. It is recommended that you do not name files matching this glob to
20 | # end with _spec.rb. You can configure this pattern with the --pattern
21 | # option on the command line or in ~/.rspec, .rspec or `.rspec-local`.
22 | #
23 | # The following line is provided for convenience purposes. It has the downside
24 | # of increasing the boot-up time by auto-requiring all files in the support
25 | # directory. Alternatively, in the individual `*_spec.rb` files, manually
26 | # require only the support files necessary.
27 | #
28 | Dir[Rails.root.join("spec", "support", "**", "*.rb")].each { |f| require f }
29 |
30 | # Checks for pending migration and applies them before tests are run.
31 | # If you are not using ActiveRecord, you can remove this line.
32 | ActiveRecord::Migration.maintain_test_schema!
33 |
34 | RSpec.configure do |config|
35 | # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
36 | config.fixture_path = "#{::Rails.root}/spec/fixtures"
37 |
38 | # If you're not using ActiveRecord, or you'd prefer not to run each of your
39 | # examples within a transaction, remove the following line or assign false
40 | # instead of true.
41 | config.use_transactional_fixtures = false
42 |
43 | # RSpec Rails can automatically mix in different behaviours to your tests
44 | # based on their file location, for example enabling you to call `get` and
45 | # `post` in specs under `spec/controllers`.
46 | #
47 | # You can disable this behaviour by removing the line below, and instead
48 | # explicitly tag your specs with their type, e.g.:
49 | #
50 | # RSpec.describe UsersController, :type => :controller do
51 | # # ...
52 | # end
53 | #
54 | # The different available types are documented in the features, such as in
55 | # https://relishapp.com/rspec/rspec-rails/docs
56 | config.infer_spec_type_from_file_location!
57 |
58 | # Filter lines from Rails gems in backtraces.
59 | config.filter_rails_from_backtrace!
60 | # arbitrary gems may also be filtered via:
61 | # config.filter_gems_from_backtrace("gem name")
62 |
63 | config.include FactoryBot::Syntax::Methods
64 | config.include RequestSpecHelper, type: :request
65 | config.include Devise::Test::ControllerHelpers, type: :controller
66 | config.extend DeviseHelper, type: :controller
67 | end
68 |
69 | Shoulda::Matchers.configure do |config|
70 | config.integrate do |with|
71 | with.test_framework :rspec
72 | with.library :rails
73 | end
74 | end
75 |
76 | RSpec::Expectations.configuration.on_potential_false_positives = :nothing
--------------------------------------------------------------------------------
/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # This file was generated by the `rails generate rspec:install` command. Conventionally, all
4 | # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
5 | # The generated `.rspec` file contains `--require spec_helper` which will cause
6 | # this file to always be loaded, without a need to explicitly require it in any
7 | # files.
8 | #
9 | # Given that it is always loaded, you are encouraged to keep this file as
10 | # light-weight as possible. Requiring heavyweight dependencies from this file
11 | # will add to the boot time of your test suite on EVERY test run, even for an
12 | # individual file that may not need all of that loaded. Instead, consider making
13 | # a separate helper file that requires the additional dependencies and performs
14 | # the additional setup, and require it from the spec files that actually need
15 | # it.
16 | #
17 | # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
18 | RSpec.configure do |config|
19 | # rspec-expectations config goes here. You can use an alternate
20 | # assertion/expectation library such as wrong or the stdlib/minitest
21 | # assertions if you prefer.
22 | config.expect_with :rspec do |expectations|
23 | # This option will default to `true` in RSpec 4. It makes the `description`
24 | # and `failure_message` of custom matchers include text for helper methods
25 | # defined using `chain`, e.g.:
26 | # be_bigger_than(2).and_smaller_than(4).description
27 | # # => "be bigger than 2 and smaller than 4"
28 | # ...rather than:
29 | # # => "be bigger than 2"
30 | expectations.include_chain_clauses_in_custom_matcher_descriptions = true
31 | end
32 |
33 | # rspec-mocks config goes here. You can use an alternate test double
34 | # library (such as bogus or mocha) by changing the `mock_with` option here.
35 | config.mock_with :rspec do |mocks|
36 | # Prevents you from mocking or stubbing a method that does not exist on
37 | # a real object. This is generally recommended, and will default to
38 | # `true` in RSpec 4.
39 | mocks.verify_partial_doubles = true
40 | end
41 |
42 | # This option will default to `:apply_to_host_groups` in RSpec 4 (and will
43 | # have no way to turn it off -- the option exists only for backwards
44 | # compatibility in RSpec 3). It causes shared context metadata to be
45 | # inherited by the metadata hash of host groups and examples, rather than
46 | # triggering implicit auto-inclusion in groups with matching metadata.
47 | config.shared_context_metadata_behavior = :apply_to_host_groups
48 |
49 | # Print the 10 slowest examples and example groups at the
50 | # end of the spec run, to help surface which specs are running
51 | # particularly slow.
52 | config.profile_examples = 10
53 |
54 | # The settings below are suggested to provide a good initial experience
55 | # with RSpec, but feel free to customize to your heart's content.
56 |
57 | # This allows you to limit a spec run to individual examples or groups
58 | # you care about by tagging them with `:focus` metadata. When nothing
59 | # is tagged with `:focus`, all examples get run. RSpec also provides
60 | # aliases for `it`, `describe`, and `context` that include `:focus`
61 | # metadata: `fit`, `fdescribe` and `fcontext`, respectively.
62 | # config.filter_run_when_matching :focus
63 |
64 | # Allows RSpec to persist some state between runs in order to support
65 | # the `--only-failures` and `--next-failure` CLI options. We recommend
66 | # you configure your source control system to ignore this file.
67 | # config.example_status_persistence_file_path = "spec/examples.txt"
68 |
69 | # Limits the available syntax to the non-monkey patched syntax that is
70 | # recommended. For more details, see:
71 | # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/
72 | # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
73 | # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode
74 | # config.disable_monkey_patching!
75 |
76 | # Many RSpec users commonly either run the entire suite or an individual
77 | # file, and it's useful to allow more verbose output when running an
78 | # individual spec file.
79 | # if config.files_to_run.one?
80 | # Use the documentation formatter for detailed output,
81 | # unless a formatter has already been configured
82 | # (e.g. via a command-line flag).
83 | # config.default_formatter = "doc"
84 | # end
85 |
86 | # Run specs in random order to surface order dependencies. If you find an
87 | # order dependency and want to debug it, you can fix the order by providing
88 | # the seed, which is printed after each run.
89 | # --seed 1234
90 | # config.order = :random
91 |
92 | # Seed global randomization in this process using the `--seed` CLI option.
93 | # Setting this allows you to use `--seed` to deterministically reproduce
94 | # test failures related to randomization by passing the same `--seed` value
95 | # as the one that triggered the failure.
96 | # Kernel.srand config.seed
97 | end
--------------------------------------------------------------------------------
/spec/support/database_cleaner.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | RSpec.configure do |config|
4 | config.before(:suite) do
5 | DatabaseCleaner.clean_with(:truncation)
6 | end
7 |
8 | config.before do
9 | DatabaseCleaner.strategy = :transaction
10 | end
11 |
12 | config.before(:each, js: true) do
13 | DatabaseCleaner.strategy = :truncation
14 | end
15 |
16 | config.before do
17 | DatabaseCleaner.start
18 | end
19 |
20 | config.after do
21 | DatabaseCleaner.clean
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/spec/support/devise_helper.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module DeviseHelper
4 | def login_user(user = nil)
5 | before do
6 | @request.env['devise.mapping'] = Devise.mappings[:user]
7 | @current_user = user || create(:user)
8 | # user.confirm!
9 | # or set a confirmed_at inside the factory.
10 | # Only necessary if you are using the "confirmable" module
11 | sign_in @current_user
12 | end
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/spec/support/request_spec_helper.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Helper class to test logged in and logged out users
4 | module RequestSpecHelper
5 | include Warden::Test::Helpers
6 |
7 | def self.included(base)
8 | base.before { Warden.test_mode! }
9 | base.after { Warden.test_reset! }
10 | end
11 |
12 | def sign_in(resource)
13 | login_as(resource, scope: warden_scope(resource))
14 | end
15 |
16 | def sign_out(resource)
17 | logout(warden_scope(resource))
18 | end
19 |
20 | private
21 |
22 | def warden_scope(resource)
23 | resource.class.name.underscore.to_sym
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/tmp/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zauberware/rails-devise-graphql/f3e71d39e9e5bb7d282af4546c65ccead6c67fb0/tmp/.keep
--------------------------------------------------------------------------------