working signup wizard redirect (Show First Run Wizard)
This commit is contained in:
parent
f8292c300f
commit
8059a1a719
|
@ -73,6 +73,13 @@ func (gr *GormRepository) CreateUser(ctx context.Context, user *models.User) err
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gr *GormRepository) GetUserCount(ctx context.Context) (int, error) {
|
||||
var count int64
|
||||
result := gr.GormClient.WithContext(ctx).Model(&models.User{}).Count(&count)
|
||||
return int(count), result.Error
|
||||
}
|
||||
|
||||
func (gr *GormRepository) GetUserByUsername(ctx context.Context, username string) (*models.User, error) {
|
||||
var foundUser models.User
|
||||
result := gr.GormClient.WithContext(ctx).Where(models.User{Username: username}).First(&foundUser)
|
||||
|
|
|
@ -14,7 +14,7 @@ type DatabaseRepository interface {
|
|||
Migrate() error
|
||||
|
||||
CreateUser(context.Context, *models.User) error
|
||||
|
||||
GetUserCount(context.Context) (int, error)
|
||||
GetUserByUsername(context.Context, string) (*models.User, error)
|
||||
GetCurrentUser(ctx context.Context) (*models.User, error)
|
||||
DeleteCurrentUser(ctx context.Context) error
|
||||
|
|
|
@ -148,6 +148,20 @@ func (mr *MockDatabaseRepositoryMockRecorder) CreateUser(arg0, arg1 interface{})
|
|||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateUser", reflect.TypeOf((*MockDatabaseRepository)(nil).CreateUser), arg0, arg1)
|
||||
}
|
||||
|
||||
// DeleteCurrentUser mocks base method.
|
||||
func (m *MockDatabaseRepository) DeleteCurrentUser(ctx context.Context) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "DeleteCurrentUser", ctx)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// DeleteCurrentUser indicates an expected call of DeleteCurrentUser.
|
||||
func (mr *MockDatabaseRepositoryMockRecorder) DeleteCurrentUser(ctx interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteCurrentUser", reflect.TypeOf((*MockDatabaseRepository)(nil).DeleteCurrentUser), ctx)
|
||||
}
|
||||
|
||||
// DeleteSource mocks base method.
|
||||
func (m *MockDatabaseRepository) DeleteSource(ctx context.Context, sourceId string) (int64, error) {
|
||||
m.ctrl.T.Helper()
|
||||
|
@ -209,13 +223,12 @@ func (mr *MockDatabaseRepositoryMockRecorder) GetCurrentUser(ctx interface{}) *g
|
|||
}
|
||||
|
||||
// GetFlattenedResourceGraph mocks base method.
|
||||
func (m *MockDatabaseRepository) GetFlattenedResourceGraph(ctx context.Context, graphType pkg.ResourceGraphType, options models.ResourceGraphOptions) (map[string][]*models.ResourceBase, *models.ResourceGraphMetadata, error) {
|
||||
func (m *MockDatabaseRepository) GetFlattenedResourceGraph(ctx context.Context, graphType pkg.ResourceGraphType, options models.ResourceGraphOptions) (map[string][]*models.ResourceBase, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetFlattenedResourceGraph", ctx, graphType, options)
|
||||
ret0, _ := ret[0].(map[string][]*models.ResourceBase)
|
||||
ret1, _ := ret[1].(*models.ResourceGraphMetadata)
|
||||
ret2, _ := ret[2].(error)
|
||||
return ret0, ret1, ret2
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetFlattenedResourceGraph indicates an expected call of GetFlattenedResourceGraph.
|
||||
|
@ -359,6 +372,21 @@ func (mr *MockDatabaseRepositoryMockRecorder) GetUserByUsername(arg0, arg1 inter
|
|||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserByUsername", reflect.TypeOf((*MockDatabaseRepository)(nil).GetUserByUsername), arg0, arg1)
|
||||
}
|
||||
|
||||
// GetUserCount mocks base method.
|
||||
func (m *MockDatabaseRepository) GetUserCount(arg0 context.Context) (int, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetUserCount", arg0)
|
||||
ret0, _ := ret[0].(int)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetUserCount indicates an expected call of GetUserCount.
|
||||
func (mr *MockDatabaseRepositoryMockRecorder) GetUserCount(arg0 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserCount", reflect.TypeOf((*MockDatabaseRepository)(nil).GetUserCount), arg0)
|
||||
}
|
||||
|
||||
// ListBackgroundJobs mocks base method.
|
||||
func (m *MockDatabaseRepository) ListBackgroundJobs(ctx context.Context, queryOptions models.BackgroundJobQueryOptions) ([]models.BackgroundJob, error) {
|
||||
m.ctrl.T.Helper()
|
||||
|
@ -389,6 +417,21 @@ func (mr *MockDatabaseRepositoryMockRecorder) ListResources(arg0, arg1 interface
|
|||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListResources", reflect.TypeOf((*MockDatabaseRepository)(nil).ListResources), arg0, arg1)
|
||||
}
|
||||
|
||||
// LoadSystemSettings mocks base method.
|
||||
func (m *MockDatabaseRepository) LoadSystemSettings(ctx context.Context) (*models.SystemSettings, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "LoadSystemSettings", ctx)
|
||||
ret0, _ := ret[0].(*models.SystemSettings)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// LoadSystemSettings indicates an expected call of LoadSystemSettings.
|
||||
func (mr *MockDatabaseRepositoryMockRecorder) LoadSystemSettings(ctx interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadSystemSettings", reflect.TypeOf((*MockDatabaseRepository)(nil).LoadSystemSettings), ctx)
|
||||
}
|
||||
|
||||
// LoadUserSettings mocks base method.
|
||||
func (m *MockDatabaseRepository) LoadUserSettings(ctx context.Context) (*models.UserSettings, error) {
|
||||
m.ctrl.T.Helper()
|
||||
|
@ -461,6 +504,20 @@ func (mr *MockDatabaseRepositoryMockRecorder) RemoveResourceAssociation(ctx, sou
|
|||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveResourceAssociation", reflect.TypeOf((*MockDatabaseRepository)(nil).RemoveResourceAssociation), ctx, source, resourceType, resourceId, relatedSource, relatedResourceType, relatedResourceId)
|
||||
}
|
||||
|
||||
// SaveSystemSettings mocks base method.
|
||||
func (m *MockDatabaseRepository) SaveSystemSettings(ctx context.Context, newSettings *models.SystemSettings) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "SaveSystemSettings", ctx, newSettings)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// SaveSystemSettings indicates an expected call of SaveSystemSettings.
|
||||
func (mr *MockDatabaseRepositoryMockRecorder) SaveSystemSettings(ctx, newSettings interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveSystemSettings", reflect.TypeOf((*MockDatabaseRepository)(nil).SaveSystemSettings), ctx, newSettings)
|
||||
}
|
||||
|
||||
// SaveUserSettings mocks base method.
|
||||
func (m *MockDatabaseRepository) SaveUserSettings(arg0 context.Context, arg1 *models.UserSettings) error {
|
||||
m.ctrl.T.Helper()
|
||||
|
@ -517,3 +574,17 @@ func (mr *MockDatabaseRepositoryMockRecorder) UpsertRawResource(ctx, sourceCrede
|
|||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpsertRawResource", reflect.TypeOf((*MockDatabaseRepository)(nil).UpsertRawResource), ctx, sourceCredentials, rawResource)
|
||||
}
|
||||
|
||||
// UpsertRawResourceAssociation mocks base method.
|
||||
func (m *MockDatabaseRepository) UpsertRawResourceAssociation(ctx context.Context, sourceId, sourceResourceType, sourceResourceId, targetSourceId, targetResourceType, targetResourceId string) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "UpsertRawResourceAssociation", ctx, sourceId, sourceResourceType, sourceResourceId, targetSourceId, targetResourceType, targetResourceId)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// UpsertRawResourceAssociation indicates an expected call of UpsertRawResourceAssociation.
|
||||
func (mr *MockDatabaseRepositoryMockRecorder) UpsertRawResourceAssociation(ctx, sourceId, sourceResourceType, sourceResourceId, targetSourceId, targetResourceType, targetResourceId interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpsertRawResourceAssociation", reflect.TypeOf((*MockDatabaseRepository)(nil).UpsertRawResourceAssociation), ctx, sourceId, sourceResourceType, sourceResourceId, targetSourceId, targetResourceType, targetResourceId)
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"embed"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/fastenhealth/fasten-onprem/backend/pkg"
|
||||
"github.com/fastenhealth/fasten-onprem/backend/pkg/config"
|
||||
"github.com/fastenhealth/fasten-onprem/backend/pkg/database"
|
||||
"github.com/fastenhealth/fasten-onprem/backend/pkg/event_bus"
|
||||
|
@ -54,15 +55,32 @@ func (ae *AppEngine) Setup() (*gin.RouterGroup, *gin.Engine) {
|
|||
{
|
||||
api.Use(middleware.CacheMiddleware())
|
||||
api.GET("/health", func(c *gin.Context) {
|
||||
// This function does a quick check to see if the server is up and running
|
||||
// it will also determine if we should show the first run wizard
|
||||
|
||||
//TODO:
|
||||
// check if the /web folder is populated.
|
||||
// check if access to database
|
||||
|
||||
//get the count of users in the DB
|
||||
databaseRepo := c.MustGet(pkg.ContextKeyTypeDatabase).(database.DatabaseRepository)
|
||||
userCount, err := databaseRepo.GetUserCount(c)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false})
|
||||
return
|
||||
}
|
||||
|
||||
keepAliveMsg := models.NewEventKeepAlive("heartbeat")
|
||||
err := ae.EventBus.PublishMessage(keepAliveMsg)
|
||||
err = ae.EventBus.PublishMessage(keepAliveMsg)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": err == nil,
|
||||
"success": true,
|
||||
"data": gin.H{
|
||||
"first_run_wizard": userCount == 0,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
@ -18,14 +18,15 @@ import {environment} from '../environments/environment';
|
|||
import {DesktopCallbackComponent} from './pages/desktop-callback/desktop-callback.component';
|
||||
import {BackgroundJobsComponent} from './pages/background-jobs/background-jobs.component';
|
||||
import {AuthSignupWizardComponent} from './pages/auth-signup-wizard/auth-signup-wizard.component';
|
||||
import {ShowFirstRunWizardGuard} from './auth-guards/show-first-run-wizard-guard';
|
||||
|
||||
const routes: Routes = [
|
||||
|
||||
{ path: 'auth/signup/wizard', component: AuthSignupWizardComponent },
|
||||
|
||||
{ path: 'auth/signin', component: AuthSigninComponent },
|
||||
{ path: 'auth/signin', component: AuthSigninComponent, canActivate: [ ShowFirstRunWizardGuard] },
|
||||
{ path: 'auth/signin/callback/:idp_type', component: AuthSigninComponent },
|
||||
{ path: 'auth/signup', component: AuthSignupComponent },
|
||||
{ path: 'auth/signup', component: AuthSignupComponent, canActivate: [ ShowFirstRunWizardGuard] },
|
||||
{ path: 'auth/signup/callback/:idp_type', component: AuthSignupComponent },
|
||||
|
||||
{ path: '', redirectTo: '/dashboard', pathMatch: 'full' },
|
||||
|
|
|
@ -41,6 +41,7 @@ import { BackgroundJobsComponent } from './pages/background-jobs/background-jobs
|
|||
import {FhirCardModule} from './components/fhir-card/fhir-card.module';
|
||||
import {FhirDatatableModule} from './components/fhir-datatable/fhir-datatable.module';
|
||||
import { AuthSignupWizardComponent } from './pages/auth-signup-wizard/auth-signup-wizard.component';
|
||||
import {ShowFirstRunWizardGuard} from './auth-guards/show-first-run-wizard-guard';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
|
@ -95,6 +96,7 @@ import { AuthSignupWizardComponent } from './pages/auth-signup-wizard/auth-signu
|
|||
deps: [AuthService, Router]
|
||||
},
|
||||
IsAuthenticatedAuthGuard,
|
||||
ShowFirstRunWizardGuard,
|
||||
{
|
||||
provide: HIGHLIGHT_OPTIONS,
|
||||
useValue: {
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import {CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree, Router} from '@angular/router';
|
||||
import {FastenApiService} from '../services/fasten-api.service';
|
||||
|
||||
@Injectable()
|
||||
export class ShowFirstRunWizardGuard implements CanActivate {
|
||||
constructor(private fastenService: FastenApiService, private router: Router) {
|
||||
|
||||
}
|
||||
|
||||
async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise <boolean> {
|
||||
try {
|
||||
//check if the server requires the first run wizard to be shown, if not, continue to login/signup
|
||||
let healthData = await this.fastenService.getHealth().toPromise()
|
||||
|
||||
if (healthData.first_run_wizard) {
|
||||
return await this.router.navigate(['/auth/signup/wizard']);
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
// if there is an error, just ignore it, and continue to the signin/signup page.
|
||||
console.error("ignoring error:", e)
|
||||
}
|
||||
// continue as normal
|
||||
return true
|
||||
}
|
||||
}
|
|
@ -71,8 +71,8 @@
|
|||
<div class="form-group form-check">
|
||||
<input [(ngModel)]="newUser.agree_terms" name="agree_terms" #agree_terms="ngModel" type="checkbox" class="form-check-input" id="agreeTermsCheck" required>
|
||||
<label class="form-check-label" for="agreeTermsCheck">
|
||||
I have read and agree to the Fasten Health <br/> <a href="https://policy.fastenhealth.com/privacy_policy.html">Privacy Policy</a>
|
||||
and <a href="https://policy.fastenhealth.com/terms.html">Terms of Service</a>
|
||||
I have read and agree to the Fasten Health <br/> <a externalLink href="https://policy.fastenhealth.com/privacy_policy.html">Privacy Policy</a>
|
||||
and <a externalLink href="https://policy.fastenhealth.com/terms.html">Terms of Service</a>
|
||||
</label>
|
||||
|
||||
<div *ngIf="agree_terms.invalid && (agree_terms.dirty || agree_terms.touched)" class="alert alert-danger">
|
||||
|
|
|
@ -56,6 +56,16 @@ export class FastenApiService {
|
|||
);
|
||||
}
|
||||
|
||||
getHealth(): Observable<any> {
|
||||
return this._httpClient.get<any>(`${GetEndpointAbsolutePath(globalThis.location, environment.fasten_api_endpoint_base)}/health`)
|
||||
.pipe(
|
||||
map((response: ResponseWrapper) => {
|
||||
console.log("Health RESPONSE", response)
|
||||
return response.data
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
SECURE ENDPOINTS
|
||||
|
|
2
go.mod
2
go.mod
|
@ -15,7 +15,7 @@ require (
|
|||
github.com/dave/jennifer v1.6.1
|
||||
github.com/dominikbraun/graph v0.15.0
|
||||
github.com/dop251/goja v0.0.0-20230605162241-28ee0ee714f3
|
||||
github.com/fastenhealth/fasten-sources v0.5.19
|
||||
github.com/fastenhealth/fasten-sources v0.5.20
|
||||
github.com/fastenhealth/gofhir-models v0.0.6
|
||||
github.com/gin-gonic/gin v1.9.0
|
||||
github.com/go-gormigrate/gormigrate/v2 v2.1.1
|
||||
|
|
4
go.sum
4
go.sum
|
@ -101,8 +101,8 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
|
|||
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fastenhealth/fasten-sources v0.5.19 h1:E5NX1LD5P6Ejk2crAxQaA/1rtnFqoiUYYYY2c/3REHQ=
|
||||
github.com/fastenhealth/fasten-sources v0.5.19/go.mod h1:wD94lmBq6F2mIwT2CBF8owU4gO7E1oY2HUX6bbovOXM=
|
||||
github.com/fastenhealth/fasten-sources v0.5.20 h1:rNEmn9wmzaa3W1uWmm+uCDjOjyAwUEf7hLrDCMdLfqk=
|
||||
github.com/fastenhealth/fasten-sources v0.5.20/go.mod h1:wD94lmBq6F2mIwT2CBF8owU4gO7E1oY2HUX6bbovOXM=
|
||||
github.com/fastenhealth/gofhir-models v0.0.6 h1:yJYYaV1eJtHiGEfA1rXLsyOm/9hIi6s2cGoZzGfW1tM=
|
||||
github.com/fastenhealth/gofhir-models v0.0.6/go.mod h1:xB8ikGxu3bUq2b1JYV+CZpHqBaLXpOizFR0eFBCunis=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
|
|
Loading…
Reference in New Issue