nquery 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +32 -0
- data/README.md +263 -24
- data/app/assets/builds/nquery/application.css +61 -4
- data/app/assets/builds/nquery/application.js +107 -12
- data/app/controllers/concerns/nquery/breadcrumbs.rb +5 -3
- data/app/controllers/concerns/nquery/chart_actions.rb +1 -0
- data/app/controllers/concerns/nquery/chart_results.rb +17 -3
- data/app/controllers/nquery/admin/data_sources_controller.rb +49 -2
- data/app/controllers/nquery/admin/groups_controller.rb +8 -2
- data/app/controllers/nquery/admin/users_controller.rb +6 -1
- data/app/controllers/nquery/application_controller.rb +45 -28
- data/app/controllers/nquery/dashboards_controller.rb +30 -0
- data/app/controllers/nquery/embed/charts_controller.rb +1 -0
- data/app/controllers/nquery/embed/dashboards_controller.rb +1 -0
- data/app/controllers/nquery/errors_controller.rb +11 -0
- data/app/controllers/nquery/home_controller.rb +1 -0
- data/app/controllers/nquery/onboarding/admins_controller.rb +42 -0
- data/app/controllers/nquery/onboarding/base_controller.rb +27 -0
- data/app/controllers/nquery/onboarding/companies_controller.rb +30 -0
- data/app/controllers/nquery/onboarding/confirmations_controller.rb +48 -0
- data/app/controllers/nquery/onboarding/congrats_controller.rb +15 -0
- data/app/controllers/nquery/queries_controller.rb +0 -1
- data/app/controllers/nquery/sessions_controller.rb +14 -17
- data/app/helpers/nquery/data_sources_helper.rb +13 -0
- data/app/helpers/nquery/groups_helper.rb +19 -0
- data/app/javascript/nquery/controllers/data_source_form_controller.js +25 -0
- data/app/mailers/nquery/application_mailer.rb +7 -0
- data/app/mailers/nquery/devise_mailer.rb +9 -0
- data/app/models/nquery/audit.rb +2 -2
- data/app/models/nquery/collection.rb +1 -1
- data/app/models/nquery/dashboard.rb +1 -1
- data/app/models/nquery/data_source.rb +139 -4
- data/app/models/nquery/organization.rb +38 -0
- data/app/models/nquery/query.rb +8 -0
- data/app/models/nquery/user.rb +45 -10
- data/app/views/nquery/admin/data_sources/_form.html.erb +121 -0
- data/app/views/nquery/admin/data_sources/edit.html.erb +6 -8
- data/app/views/nquery/admin/data_sources/new.html.erb +6 -7
- data/app/views/nquery/admin/groups/_form.html.erb +47 -0
- data/app/views/nquery/admin/groups/edit.html.erb +15 -6
- data/app/views/nquery/admin/groups/index.html.erb +25 -20
- data/app/views/nquery/admin/groups/new.html.erb +6 -6
- data/app/views/nquery/admin/groups/show.html.erb +98 -19
- data/app/views/nquery/admin/logs/index.html.erb +26 -30
- data/app/views/nquery/charts/_builder_form.html.erb +6 -4
- data/app/views/nquery/charts/edit.html.erb +2 -1
- data/app/views/nquery/dashboards/index.html.erb +6 -0
- data/app/views/nquery/dashboards/new.html.erb +34 -0
- data/app/views/nquery/devise_mailer/confirmation_instructions.html.erb +5 -0
- data/app/views/nquery/devise_mailer/confirmation_instructions.text.erb +5 -0
- data/app/views/nquery/errors/internal_server_error.html.erb +10 -0
- data/app/views/nquery/errors/not_found.html.erb +10 -0
- data/app/views/nquery/home/index.html.erb +7 -15
- data/app/views/nquery/onboarding/admins/new.html.erb +21 -0
- data/app/views/nquery/onboarding/companies/new.html.erb +25 -0
- data/app/views/nquery/onboarding/confirmations/show.html.erb +17 -0
- data/app/views/nquery/onboarding/congrats/show.html.erb +11 -0
- data/app/views/nquery/sessions/new.html.erb +8 -9
- data/app/views/nquery/shared/_auth_shell.html.erb +1 -1
- data/app/views/nquery/shared/_topbar.html.erb +0 -4
- data/config/routes.rb +26 -7
- data/db/migrate/20260721000001_create_nquery_tables.rb +1 -1
- data/db/migrate/20260722000001_add_key_to_nquery_data_sources.rb +8 -0
- data/db/migrate/20260722000002_create_nquery_organizations.rb +11 -0
- data/db/migrate/20260722000003_add_devise_to_nquery_users.rb +35 -0
- data/db/migrate/20260722000004_add_onboarding_completed_at_to_nquery_organizations.rb +7 -0
- data/lib/generators/nquery/install_generator.rb +9 -17
- data/lib/nquery/configuration.rb +7 -23
- data/lib/nquery/csv_importer.rb +3 -3
- data/lib/nquery/data_sources/adapter.rb +4 -0
- data/lib/nquery/data_sources/postgresql_adapter.rb +15 -1
- data/lib/nquery/data_sources/rails_adapter.rb +5 -0
- data/lib/nquery/data_sources/syncer.rb +50 -0
- data/lib/nquery/encryption.rb +50 -0
- data/lib/nquery/engine.rb +42 -3
- data/lib/nquery/onboarding/admin_provisioner.rb +40 -0
- data/lib/nquery/onboarding/password_confirmation.rb +46 -0
- data/lib/nquery/onboarding.rb +40 -0
- data/lib/nquery/permissions/resolver.rb +14 -4
- data/lib/nquery/query_runner.rb +5 -9
- data/lib/nquery/readonly_sql.rb +25 -0
- data/lib/nquery/seeder.rb +25 -34
- data/lib/nquery/setup.rb +75 -0
- data/lib/nquery/version.rb +1 -1
- data/lib/nquery.rb +9 -0
- data/lib/tasks/nquery.rake +6 -0
- data/nquery.gemspec +11 -5
- metadata +135 -26
- data/app/controllers/nquery/registrations_controller.rb +0 -31
- data/app/views/nquery/registrations/new.html.erb +0 -34
|
@@ -9,14 +9,28 @@ module Nquery
|
|
|
9
9
|
def chart_result(chart)
|
|
10
10
|
return demo_result unless chart.query&.statement.present?
|
|
11
11
|
|
|
12
|
+
chart_query_result(chart)
|
|
13
|
+
rescue StandardError
|
|
14
|
+
demo_result
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def chart_builder_result(chart)
|
|
18
|
+
return nil unless chart.query&.statement.present?
|
|
19
|
+
|
|
20
|
+
chart_query_result(chart, audit: false)
|
|
21
|
+
rescue QueryRunner::PermissionError, QueryRunner::Error => e
|
|
22
|
+
{ error: e.message }
|
|
23
|
+
rescue StandardError => e
|
|
24
|
+
{ error: e.message }
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def chart_query_result(chart, audit: true)
|
|
12
28
|
QueryRunner.new(
|
|
13
29
|
data_source: chart.query.data_source || DataSource.first,
|
|
14
30
|
statement: chart.query.statement,
|
|
15
31
|
user: current_nquery_user,
|
|
16
32
|
query: chart.query
|
|
17
|
-
).run
|
|
18
|
-
rescue StandardError
|
|
19
|
-
demo_result
|
|
33
|
+
).run(audit: audit)
|
|
20
34
|
end
|
|
21
35
|
|
|
22
36
|
def demo_result
|
|
@@ -15,6 +15,8 @@ module Nquery
|
|
|
15
15
|
|
|
16
16
|
def create
|
|
17
17
|
@data_source = DataSource.new(data_source_params)
|
|
18
|
+
@data_source.connection_fields_submitted = connection_fields_required?(@data_source.adapter)
|
|
19
|
+
|
|
18
20
|
if @data_source.save
|
|
19
21
|
redirect_to admin_data_sources_path, notice: "Data source created."
|
|
20
22
|
else
|
|
@@ -23,24 +25,69 @@ module Nquery
|
|
|
23
25
|
end
|
|
24
26
|
|
|
25
27
|
def edit
|
|
28
|
+
@data_source.assign_connection_fields_from_config
|
|
26
29
|
end
|
|
27
30
|
|
|
28
31
|
def update
|
|
29
|
-
|
|
32
|
+
@data_source.assign_attributes(data_source_params)
|
|
33
|
+
@data_source.connection_fields_submitted = connection_fields_required?(@data_source.adapter)
|
|
34
|
+
|
|
35
|
+
if @data_source.save
|
|
30
36
|
redirect_to admin_data_sources_path, notice: "Data source updated."
|
|
31
37
|
else
|
|
38
|
+
@data_source.assign_connection_fields_from_config unless @data_source.connection_fields_submitted?
|
|
32
39
|
render :edit, status: :unprocessable_content
|
|
33
40
|
end
|
|
34
41
|
end
|
|
35
42
|
|
|
43
|
+
def test_connection
|
|
44
|
+
@data_source = data_source_for_connection_test
|
|
45
|
+
unless @data_source
|
|
46
|
+
render json: { ok: false, error: "Data source not found." }, status: :not_found
|
|
47
|
+
return
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
@data_source.assign_attributes(data_source_params)
|
|
51
|
+
@data_source.connection_fields_submitted = connection_fields_required?(@data_source.adapter)
|
|
52
|
+
|
|
53
|
+
if @data_source.test_connection
|
|
54
|
+
render json: { ok: true, message: "Connection successful." }
|
|
55
|
+
else
|
|
56
|
+
render json: { ok: false, error: @data_source.errors.full_messages.to_sentence },
|
|
57
|
+
status: :unprocessable_content
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
36
61
|
private
|
|
37
62
|
|
|
38
63
|
def set_data_source
|
|
39
64
|
@data_source = DataSource.find(params[:id])
|
|
40
65
|
end
|
|
41
66
|
|
|
67
|
+
def data_source_for_connection_test
|
|
68
|
+
id = params[:data_source_id].presence
|
|
69
|
+
return DataSource.new if id.blank?
|
|
70
|
+
|
|
71
|
+
DataSource.find_by(id: id)
|
|
72
|
+
end
|
|
73
|
+
|
|
42
74
|
def data_source_params
|
|
43
|
-
params.require(:data_source).permit(
|
|
75
|
+
params.require(:data_source).permit(
|
|
76
|
+
:name,
|
|
77
|
+
:adapter,
|
|
78
|
+
:active,
|
|
79
|
+
:host,
|
|
80
|
+
:port,
|
|
81
|
+
:database,
|
|
82
|
+
:username,
|
|
83
|
+
:password,
|
|
84
|
+
:database_path,
|
|
85
|
+
:sslmode
|
|
86
|
+
)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def connection_fields_required?(adapter)
|
|
90
|
+
DataSource::REMOTE_ADAPTERS.include?(adapter) || adapter == "sqlite"
|
|
44
91
|
end
|
|
45
92
|
end
|
|
46
93
|
end
|
|
@@ -10,7 +10,8 @@ module Nquery
|
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
def show
|
|
13
|
-
@
|
|
13
|
+
@members = @group.users.order(:first_name, :last_name, :email)
|
|
14
|
+
@available_users = User.active.where.not(id: @group.user_ids).order(:first_name, :last_name, :email)
|
|
14
15
|
end
|
|
15
16
|
|
|
16
17
|
def new
|
|
@@ -44,8 +45,13 @@ module Nquery
|
|
|
44
45
|
end
|
|
45
46
|
|
|
46
47
|
def remove_member
|
|
48
|
+
if @group.system_group == "all_users"
|
|
49
|
+
redirect_to admin_group_path(@group), alert: "Members cannot be removed from the All Users group."
|
|
50
|
+
return
|
|
51
|
+
end
|
|
52
|
+
|
|
47
53
|
membership = @group.group_memberships.find_by!(user_id: params[:user_id])
|
|
48
|
-
membership.destroy
|
|
54
|
+
membership.destroy!
|
|
49
55
|
redirect_to admin_group_path(@group), notice: "Member removed."
|
|
50
56
|
end
|
|
51
57
|
|
|
@@ -23,7 +23,12 @@ module Nquery
|
|
|
23
23
|
assign_groups(@user)
|
|
24
24
|
@user.ensure_all_users_membership!
|
|
25
25
|
@user.ensure_personal_collection!
|
|
26
|
-
|
|
26
|
+
if @user.last_devise_notification_delivered == false
|
|
27
|
+
redirect_to admin_users_path,
|
|
28
|
+
alert: "User invited, but the confirmation email could not be sent. Check your mailer settings."
|
|
29
|
+
else
|
|
30
|
+
redirect_to admin_users_path, notice: "User invited. A confirmation email was sent."
|
|
31
|
+
end
|
|
27
32
|
else
|
|
28
33
|
@groups = Group.order(:name)
|
|
29
34
|
render :new, status: :unprocessable_content
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module Nquery
|
|
4
4
|
class ApplicationController < ActionController::Base
|
|
5
|
+
include Devise::Controllers::Helpers
|
|
5
6
|
include Nquery::AuthorizesCollection
|
|
6
7
|
include Nquery::Breadcrumbs
|
|
7
8
|
include Nquery::Engine.routes.url_helpers
|
|
@@ -9,50 +10,66 @@ module Nquery
|
|
|
9
10
|
protect_from_forgery with: :exception
|
|
10
11
|
layout "nquery/application"
|
|
11
12
|
|
|
12
|
-
helper_method :current_nquery_user, :permission_resolver
|
|
13
|
+
helper_method :current_nquery_user, :permission_resolver, :login_path, :logout_path
|
|
13
14
|
|
|
14
|
-
before_action :
|
|
15
|
+
before_action :redirect_to_onboarding, unless: :skip_onboarding_redirect?
|
|
16
|
+
before_action :authenticate_nquery_user!, unless: :auth_exempt?
|
|
15
17
|
before_action :set_current_user_context
|
|
16
18
|
|
|
19
|
+
rescue_from StandardError, with: :render_internal_server_error
|
|
20
|
+
rescue_from ActiveRecord::RecordNotFound, with: :render_not_found
|
|
21
|
+
|
|
17
22
|
private
|
|
18
23
|
|
|
19
|
-
def
|
|
20
|
-
if
|
|
21
|
-
instance_eval(&Nquery.configuration.authenticate)
|
|
22
|
-
else
|
|
23
|
-
default_authenticate!
|
|
24
|
-
end
|
|
25
|
-
end
|
|
24
|
+
def redirect_to_onboarding
|
|
25
|
+
return if Onboarding.complete?
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
return if auth_exempt?
|
|
27
|
+
redirect_to new_onboarding_company_path
|
|
28
|
+
end
|
|
30
29
|
|
|
31
|
-
|
|
30
|
+
def skip_onboarding_redirect?
|
|
31
|
+
auth_exempt? || Onboarding.complete?
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
def auth_exempt?
|
|
35
|
-
controller_path.in?(%w[
|
|
35
|
+
controller_path.in?(%w[
|
|
36
|
+
nquery/sessions
|
|
37
|
+
nquery/onboarding/companies
|
|
38
|
+
nquery/onboarding/admins
|
|
39
|
+
nquery/onboarding/congrats
|
|
40
|
+
nquery/onboarding/confirmations
|
|
41
|
+
nquery/embed/charts
|
|
42
|
+
nquery/embed/dashboards
|
|
43
|
+
nquery/errors
|
|
44
|
+
])
|
|
36
45
|
end
|
|
37
46
|
|
|
38
|
-
def
|
|
39
|
-
|
|
47
|
+
def render_not_found(_error = nil)
|
|
48
|
+
render template: "nquery/errors/not_found", layout: "nquery/auth", status: :not_found
|
|
40
49
|
end
|
|
41
50
|
|
|
42
|
-
def
|
|
43
|
-
if
|
|
44
|
-
return User.active.find_by(id: session[:nquery_user_id])
|
|
45
|
-
end
|
|
51
|
+
def render_internal_server_error(error)
|
|
52
|
+
raise error if show_detailed_exceptions?
|
|
46
53
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
+
Rails.logger.error("[nquery] #{error.class}: #{error.message}\n#{error.backtrace&.first(15)&.join("\n")}")
|
|
55
|
+
render template: "nquery/errors/internal_server_error", layout: "nquery/auth", status: :internal_server_error
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def show_detailed_exceptions?
|
|
59
|
+
Rails.application.config.consider_all_requests_local
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def login_path(**options)
|
|
64
|
+
new_nquery_user_session_path(**options)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def logout_path(**options)
|
|
68
|
+
destroy_nquery_user_session_path(**options)
|
|
69
|
+
end
|
|
54
70
|
|
|
55
|
-
|
|
71
|
+
def sign_in_nquery_user(user)
|
|
72
|
+
sign_in(:nquery_user, user)
|
|
56
73
|
end
|
|
57
74
|
|
|
58
75
|
def set_current_user_context
|
|
@@ -11,6 +11,29 @@ module Nquery
|
|
|
11
11
|
|
|
12
12
|
def index
|
|
13
13
|
@dashboards = filter_viewable_dashboards(Dashboard.active.includes(:collection).order(:name))
|
|
14
|
+
@collections = assignable_collections
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def new
|
|
18
|
+
@collections = assignable_collections
|
|
19
|
+
if @collections.empty?
|
|
20
|
+
redirect_to dashboards_path, alert: "You do not have permission to create a dashboard."
|
|
21
|
+
return
|
|
22
|
+
end
|
|
23
|
+
@dashboard = Dashboard.new(collection: default_dashboard_collection)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def create
|
|
27
|
+
@collections = assignable_collections
|
|
28
|
+
@dashboard = Dashboard.new(dashboard_params.merge(creator: current_nquery_user))
|
|
29
|
+
authorize_collection_access!(@dashboard.collection, required: :curate) if @dashboard.collection
|
|
30
|
+
return if performed?
|
|
31
|
+
|
|
32
|
+
if @dashboard.save
|
|
33
|
+
redirect_to dashboard_path(@dashboard), notice: "Dashboard created."
|
|
34
|
+
else
|
|
35
|
+
render :new, status: :unprocessable_content
|
|
36
|
+
end
|
|
14
37
|
end
|
|
15
38
|
|
|
16
39
|
def show
|
|
@@ -86,5 +109,12 @@ module Nquery
|
|
|
86
109
|
def dashboard_params
|
|
87
110
|
params.require(:dashboard).permit(:name, :description, :collection_id, settings: {})
|
|
88
111
|
end
|
|
112
|
+
|
|
113
|
+
def default_dashboard_collection
|
|
114
|
+
root = Collection.roots.first
|
|
115
|
+
return root if root && @collections.include?(root)
|
|
116
|
+
|
|
117
|
+
@collections.first
|
|
118
|
+
end
|
|
89
119
|
end
|
|
90
120
|
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Nquery
|
|
4
|
+
module Onboarding
|
|
5
|
+
class AdminsController < BaseController
|
|
6
|
+
before_action :redirect_if_pending_confirmation!, only: %i[new create]
|
|
7
|
+
before_action :require_organization!
|
|
8
|
+
|
|
9
|
+
def new
|
|
10
|
+
@user = User.new
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def create
|
|
14
|
+
@user = User.new(admin_params)
|
|
15
|
+
if @user.valid?
|
|
16
|
+
result = Onboarding::AdminProvisioner.call(@user)
|
|
17
|
+
session[:onboarding_admin_email] = result.user.email
|
|
18
|
+
if result.mail_delivered?
|
|
19
|
+
redirect_to onboarding_congrats_path
|
|
20
|
+
else
|
|
21
|
+
redirect_to onboarding_congrats_path,
|
|
22
|
+
alert: "Your admin account was created, but the confirmation email could not be sent. Check your mailer settings."
|
|
23
|
+
end
|
|
24
|
+
else
|
|
25
|
+
render :new, status: :unprocessable_content
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def require_organization!
|
|
32
|
+
return if session[:onboarding_organization_id].present? && Organization.exists?(id: session[:onboarding_organization_id])
|
|
33
|
+
|
|
34
|
+
redirect_to new_onboarding_company_path, alert: "Please set up your company first."
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def admin_params
|
|
38
|
+
params.require(:user).permit(:email, :first_name, :last_name)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Nquery
|
|
4
|
+
module Onboarding
|
|
5
|
+
class BaseController < ApplicationController
|
|
6
|
+
skip_before_action :authenticate_nquery_user!
|
|
7
|
+
skip_before_action :redirect_to_onboarding
|
|
8
|
+
before_action :ensure_onboarding_available!
|
|
9
|
+
|
|
10
|
+
layout "nquery/auth"
|
|
11
|
+
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
def ensure_onboarding_available!
|
|
15
|
+
return unless Onboarding.complete?
|
|
16
|
+
|
|
17
|
+
redirect_to login_path, alert: "Onboarding has already been completed."
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def redirect_if_pending_confirmation!
|
|
21
|
+
return unless Onboarding.pending_admin_confirmation?
|
|
22
|
+
|
|
23
|
+
redirect_to onboarding_congrats_path, alert: "Please confirm your email to continue setup."
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Nquery
|
|
4
|
+
module Onboarding
|
|
5
|
+
class CompaniesController < BaseController
|
|
6
|
+
before_action :redirect_if_pending_confirmation!, only: %i[new create]
|
|
7
|
+
|
|
8
|
+
def new
|
|
9
|
+
@organization = Organization.new
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def create
|
|
13
|
+
@organization = Organization.first_or_initialize
|
|
14
|
+
@organization.assign_attributes(organization_params)
|
|
15
|
+
if @organization.save
|
|
16
|
+
session[:onboarding_organization_id] = @organization.id
|
|
17
|
+
redirect_to new_onboarding_admin_path
|
|
18
|
+
else
|
|
19
|
+
render :new, status: :unprocessable_content
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def organization_params
|
|
26
|
+
params.require(:organization).permit(:name, :website, :logo, :cover_image)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Nquery
|
|
4
|
+
module Onboarding
|
|
5
|
+
class ConfirmationsController < BaseController
|
|
6
|
+
skip_before_action :ensure_onboarding_available!
|
|
7
|
+
|
|
8
|
+
def show
|
|
9
|
+
@user = find_confirmable_user
|
|
10
|
+
redirect_to login_path, alert: "Invalid confirmation link." unless @user
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def update
|
|
14
|
+
@user = find_confirmable_user
|
|
15
|
+
return redirect_to login_path, alert: "Invalid confirmation link." unless @user
|
|
16
|
+
|
|
17
|
+
result = Onboarding::PasswordConfirmation.call(
|
|
18
|
+
user: @user,
|
|
19
|
+
password: confirmation_params[:password],
|
|
20
|
+
password_confirmation: confirmation_params[:password_confirmation]
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
if result.success?
|
|
24
|
+
sign_in_nquery_user(result.user)
|
|
25
|
+
redirect_to root_path, notice: "Your account is ready. Welcome to nquery!"
|
|
26
|
+
else
|
|
27
|
+
render :show, status: :unprocessable_content
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def find_confirmable_user
|
|
34
|
+
token = params[:confirmation_token].to_s
|
|
35
|
+
return if token.blank?
|
|
36
|
+
|
|
37
|
+
user = User.find_by(confirmation_token: token, confirmed_at: nil)
|
|
38
|
+
return if user.nil? || user.confirmation_expired?
|
|
39
|
+
|
|
40
|
+
user
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def confirmation_params
|
|
44
|
+
params.require(:user).permit(:password, :password_confirmation)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Nquery
|
|
4
|
+
module Onboarding
|
|
5
|
+
class CongratsController < BaseController
|
|
6
|
+
def show
|
|
7
|
+
@email = session[:onboarding_admin_email]
|
|
8
|
+
return if @email.present?
|
|
9
|
+
return if Onboarding.pending_admin_confirmation?
|
|
10
|
+
|
|
11
|
+
redirect_to new_onboarding_company_path, alert: "Please complete onboarding."
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
module Nquery
|
|
4
4
|
class QueriesController < ApplicationController
|
|
5
|
-
skip_before_action :_authenticate!, only: [] # always auth
|
|
6
5
|
before_action :set_query, only: %i[show edit update]
|
|
7
6
|
before_action :authorize_query_collection!, only: %i[show edit update]
|
|
8
7
|
before_action :set_data_source_for_run, only: %i[run schema]
|
|
@@ -1,28 +1,25 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Nquery
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
# Inherits Devise::SessionsController (→ host ApplicationController via Devise's
|
|
5
|
+
# global parent_controller). Intentionally does NOT inherit Nquery::ApplicationController
|
|
6
|
+
# so engine auth/onboarding callbacks are not applied to the sign-in flow.
|
|
7
|
+
class SessionsController < Devise::SessionsController
|
|
8
|
+
include Nquery::Engine.routes.url_helpers
|
|
9
|
+
|
|
10
|
+
helper Nquery::ComponentsHelper
|
|
11
|
+
helper Nquery::IconHelper
|
|
12
|
+
|
|
6
13
|
layout "nquery/auth"
|
|
7
14
|
|
|
8
|
-
|
|
9
|
-
redirect_to root_path if current_nquery_user
|
|
10
|
-
end
|
|
15
|
+
private
|
|
11
16
|
|
|
12
|
-
def
|
|
13
|
-
|
|
14
|
-
if user&.authenticate(params[:password])
|
|
15
|
-
session[:nquery_user_id] = user.id
|
|
16
|
-
redirect_to root_path, notice: "Signed in successfully."
|
|
17
|
-
else
|
|
18
|
-
flash.now[:alert] = "Invalid email or password."
|
|
19
|
-
render :new, status: :unprocessable_content
|
|
20
|
-
end
|
|
17
|
+
def after_sign_in_path_for(_resource)
|
|
18
|
+
stored_location_for(:nquery_user) || root_path
|
|
21
19
|
end
|
|
22
20
|
|
|
23
|
-
def
|
|
24
|
-
|
|
25
|
-
redirect_to login_path, notice: "Signed out."
|
|
21
|
+
def after_sign_out_path_for(_resource_or_scope)
|
|
22
|
+
new_nquery_user_session_path
|
|
26
23
|
end
|
|
27
24
|
end
|
|
28
25
|
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Nquery
|
|
4
|
+
module DataSourcesHelper
|
|
5
|
+
def data_source_form_url(data_source)
|
|
6
|
+
data_source.persisted? ? admin_data_source_path(data_source) : admin_data_sources_path
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def data_source_form_method(data_source)
|
|
10
|
+
data_source.persisted? ? :patch : :post
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Nquery
|
|
4
|
+
module GroupsHelper
|
|
5
|
+
GROUP_TYPE_LABELS = {
|
|
6
|
+
"administrators" => "Administrators",
|
|
7
|
+
"all_users" => "All users",
|
|
8
|
+
"custom" => "Custom"
|
|
9
|
+
}.freeze
|
|
10
|
+
|
|
11
|
+
def group_type_label(group)
|
|
12
|
+
GROUP_TYPE_LABELS.fetch(group.system_group, group.system_group.humanize)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def group_type_badge_class(group)
|
|
16
|
+
group.system? ? "nq-badge-system" : "nq-badge-custom"
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
|
|
3
|
+
export default class extends Controller {
|
|
4
|
+
static targets = ["adapter", "remoteFields", "sqliteFields", "railsHint", "port", "testButton", "testStatus"]
|
|
5
|
+
static values = { adapter: String, testUrl: String, id: String }
|
|
6
|
+
|
|
7
|
+
connect() {
|
|
8
|
+
this.toggle()
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
toggle() {
|
|
12
|
+
const adapter = this.adapterTarget?.value || this.element.dataset.dataSourceFormAdapterValue || "postgresql"
|
|
13
|
+
const isRemote = adapter === "postgresql" || adapter === "mysql"
|
|
14
|
+
const isSqlite = adapter === "sqlite"
|
|
15
|
+
const isRails = adapter === "rails"
|
|
16
|
+
|
|
17
|
+
this.remoteFieldsTarget?.toggleAttribute("hidden", !isRemote)
|
|
18
|
+
this.sqliteFieldsTarget?.toggleAttribute("hidden", !isSqlite)
|
|
19
|
+
this.railsHintTarget?.toggleAttribute("hidden", !isRails)
|
|
20
|
+
|
|
21
|
+
if (this.hasPortTarget) {
|
|
22
|
+
this.portTarget.placeholder = adapter === "mysql" ? "3306" : "5432"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
data/app/models/nquery/audit.rb
CHANGED
|
@@ -11,9 +11,9 @@ module Nquery
|
|
|
11
11
|
pattern = "%#{sanitize_sql_like(term.downcase)}%"
|
|
12
12
|
full_name_sql = if connection.adapter_name.downcase.include?("mysql")
|
|
13
13
|
"CONCAT(nquery_users.first_name, ' ', nquery_users.last_name)"
|
|
14
|
-
|
|
14
|
+
else
|
|
15
15
|
"nquery_users.first_name || ' ' || nquery_users.last_name"
|
|
16
|
-
|
|
16
|
+
end
|
|
17
17
|
|
|
18
18
|
joins(:user).where(
|
|
19
19
|
"LOWER(nquery_users.email) LIKE :q OR LOWER(#{full_name_sql}) LIKE :q",
|
|
@@ -10,7 +10,7 @@ module Nquery
|
|
|
10
10
|
has_many :collection_permissions, class_name: "Nquery::CollectionPermission", dependent: :destroy
|
|
11
11
|
has_many :queries, class_name: "Nquery::Query", dependent: :nullify
|
|
12
12
|
has_many :charts, class_name: "Nquery::Chart", dependent: :nullify
|
|
13
|
-
has_many :dashboards, class_name: "Nquery::Dashboard", dependent: :
|
|
13
|
+
has_many :dashboards, class_name: "Nquery::Dashboard", dependent: :destroy
|
|
14
14
|
|
|
15
15
|
validates :name, presence: true
|
|
16
16
|
validates :kind, inclusion: { in: KINDS }
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module Nquery
|
|
4
4
|
class Dashboard < ApplicationRecord
|
|
5
|
-
belongs_to :collection, class_name: "Nquery::Collection"
|
|
5
|
+
belongs_to :collection, class_name: "Nquery::Collection"
|
|
6
6
|
belongs_to :creator, class_name: "Nquery::User", optional: true
|
|
7
7
|
has_many :dashboard_cards, class_name: "Nquery::DashboardCard", dependent: :destroy
|
|
8
8
|
has_many :charts, through: :dashboard_cards, class_name: "Nquery::Chart"
|