Skip to content

Commit

Permalink
Merge pull request #2666 from IDEMSInternational/feat/user-profile-re…
Browse files Browse the repository at this point in the history
…store

Feat!: Auth user db sync
  • Loading branch information
chrismclarke authored Jan 2, 2025
2 parents b6936d4 + 57c4ad4 commit 6af7ee8
Show file tree
Hide file tree
Showing 13 changed files with 116 additions and 15 deletions.
2 changes: 1 addition & 1 deletion packages/api/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "api",
"version": "1.5.1",
"version": "1.6.0",
"scripts": {
"prebuild": "rimraf dist",
"build": "nest build",
Expand Down
35 changes: 34 additions & 1 deletion packages/api/spec-export.json
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,39 @@
]
}
},
"/auth_users/{auth_user_id}": {
"get": {
"operationId": "AuthUsersController_findOne",
"summary": "Get auth user profile",
"parameters": [
{
"name": "auth_user_id",
"required": true,
"in": "path",
"schema": {
"type": "string"
}
},
{
"name": "x-deployment-db-name",
"in": "header",
"description": "Name of db for deployment to populate",
"schema": {
"type": "string",
"default": "plh"
}
}
],
"responses": {
"200": {
"description": ""
}
},
"tags": [
"Auth Users"
]
}
},
"/contact_fields": {
"get": {
"operationId": "ContactFieldController_findAll",
Expand Down Expand Up @@ -339,7 +372,7 @@
"info": {
"title": "IDEMS Apps API",
"description": "App-Server Communication",
"version": "1.4.4",
"version": "1.6.0",
"contact": {}
},
"tags": [
Expand Down
1 change: 1 addition & 0 deletions packages/api/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { DeploymentModule } from "./modules";
}),
DefaultModule,
Endpoints.AppUsersModule,
Endpoints.AuthUsersModule,
Endpoints.ContactFieldsModule,
Endpoints.AppFeedbackModule,
Endpoints.AppNotificationInteractionModule,
Expand Down
9 changes: 5 additions & 4 deletions packages/api/src/db/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,15 @@ export class DBInstance {
database: this.dbName,
// disable verbose migration logs in test
logging: process.env.NODE_ENV === "test" ? false : true,
// Fix SSL issue
// Enable ssl mode when running on production
// https://dev.to/rodjosh/connectionerror-sequelizeconnectionerror-no-pghbaconf-entry-for-host-in-heroku-postgresql-using-sequelize-3icj
dialectOptions: {
ssl: {
// https://stackoverflow.com/a/61411969
dialectOptions: environment.production ? {
ssl: {
require: true,
rejectUnauthorized: false,
},
},
} : {},
});
await this.runMigrations(migrationClient);
await migrationClient.close();
Expand Down
13 changes: 13 additions & 0 deletions packages/api/src/db/migrations/008-auth-user-column.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { DataTypes, QueryInterface } from "sequelize";
import { UmzugOptions } from "umzug";

export const up = async (options: UmzugOptions<QueryInterface>) => {
const queryInterface = options.context as QueryInterface;
await queryInterface.addColumn("app_users", "auth_user_id", {
type: DataTypes.STRING,
});
};
export const down = async (options: UmzugOptions<QueryInterface>) => {
const queryInterface = options.context as QueryInterface;
await queryInterface.removeColumn("app_users", "auth_user_id");
};
3 changes: 3 additions & 0 deletions packages/api/src/endpoints/app_users/app_user.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ export class AppUser extends Model<InferAttributes<AppUser>, InferCreationAttrib
@Column({ allowNull: false })
app_deployment_name: string;

@Column
auth_user_id: string;

@Column({ type: DataType.JSONB })
contact_fields: any;

Expand Down
24 changes: 24 additions & 0 deletions packages/api/src/endpoints/auth_users/auth_user.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Controller, Get, Param, } from "@nestjs/common";
import { ApiOperation, ApiParam, ApiTags } from "@nestjs/swagger";
import { DeploymentHeaders } from "src/modules/deployment.decorators";
import { DeploymentService } from "src/modules/deployment.service";
import { AppUser } from "../app_users/app_user.model";

@ApiTags("Auth Users")
@Controller("auth_users")
/**
* The auth_users endpoint are a thin wrapper around the app_users table to enable
* user lookup by auth_user_id
*/
export class AuthUsersController {
constructor(private readonly deploymentService: DeploymentService) {}

@Get(":auth_user_id")
@ApiParam({ name: "auth_user_id", type: String })
@ApiOperation({ summary: "Get auth user profile" })
@DeploymentHeaders()
findOne(@Param("auth_user_id") auth_user_id: string): Promise<AppUser[]> {
const model = this.deploymentService.model(AppUser);
return model.findAll({where:{auth_user_id}})
}
}
7 changes: 7 additions & 0 deletions packages/api/src/endpoints/auth_users/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { AuthUsersController } from './auth_user.controller';
import { Module } from '@nestjs/common';

@Module({
controllers: [AuthUsersController],
})
export class AuthUsersModule {}
1 change: 1 addition & 0 deletions packages/api/src/endpoints/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from "./app_users";
export * from './auth_users'
export * from "./contact_fields";
export * from "./tables";
export * from "./app_feedback";
Expand Down
2 changes: 1 addition & 1 deletion packages/data-models/fields.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
* They are used to store store computed or exported variables and are not user overridable
*/
enum PROTECTED_FIELDS {
APP_AUTH_USER = "app_auth_user",
APP_FIRST_LAUNCH = "app_first_launch",
APP_LANGUAGE = "app_language",
APP_SKIN = "app_skin",
Expand All @@ -14,6 +13,7 @@ enum PROTECTED_FIELDS {
APP_UPDATE_DOWNLOADED = "app_update_downloaded",
APP_USER_ID = "app_user_id",
APP_VERSION = "app_version",
AUTH_USER_ID = "auth_user_id",
CONTENT_VERSION = "content_version",
DEPLOYMENT_NAME = "deployment_name",
FEEDBACK_SELECTED_TEXT = "feedback_selected_text",
Expand Down
2 changes: 1 addition & 1 deletion packages/server/docker/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ services:
# context: ../../api
# dockerfile: Dockerfile
# target: prod-env
image: idems/apps-api:1.5.1
image: idems/apps-api:1.6.0
env_file:
- ../../api/.env
environment:
Expand Down
28 changes: 21 additions & 7 deletions src/app/shared/services/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { IAuthUser } from "./types";
import { filter, firstValueFrom, tap } from "rxjs";
import { TemplateService } from "../../components/template/services/template.service";
import { toObservable } from "@angular/core/rxjs-interop";
import { ServerService } from "../server/server.service";

@Injectable({
providedIn: "root",
Expand All @@ -22,14 +23,20 @@ export class AuthService extends AsyncServiceBase {
private localStorageService: LocalStorageService,
private deploymentService: DeploymentService,
private injector: Injector,
private templateService: TemplateService
private templateService: TemplateService,
private serverService: ServerService
) {
super("Auth");
this.provider = getAuthProvider(this.config.provider);
this.registerInitFunction(this.initialise);
effect(async () => {
const authUser = this.provider.authUser();
await this.checkProfileRestore(authUser);
this.addStorageEntry(authUser);
// perform immediate sync if user signed in to ensure data backed up
if (authUser) {
this.serverService.syncUserData();
}
});
}

Expand All @@ -46,6 +53,14 @@ export class AuthService extends AsyncServiceBase {
}
}

private async checkProfileRestore(authUser?: IAuthUser) {
if (!authUser) return;
const existingUser = this.localStorageService.getProtected("AUTH_USER_ID");
if (existingUser) return;
// no existingUser user, should check if authUser exists on server and prompt restore
// TODO - handle
}

private async enforceLogin(templateName: string) {
// If user already logged in simply return. If providers auto-login during then waiting to verify
// should be included during the provide init method
Expand Down Expand Up @@ -91,13 +106,12 @@ export class AuthService extends AsyncServiceBase {
});
}

/** Keep a subset of auth user info in contact fields for db lookup*/
private addStorageEntry(user?: IAuthUser) {
if (user) {
const { uid } = user;
this.localStorageService.setProtected("APP_AUTH_USER", JSON.stringify({ uid }));
/** Keep id of auth user info in contact fields for db lookup*/
private addStorageEntry(auth_user?: IAuthUser) {
if (auth_user) {
this.localStorageService.setProtected("AUTH_USER_ID", auth_user.uid);
} else {
this.localStorageService.removeProtected("APP_AUTH_USER");
this.localStorageService.removeProtected("AUTH_USER_ID");
}
}
}
4 changes: 4 additions & 0 deletions src/app/shared/services/server/server.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,18 @@ export class ServerService extends SyncServiceBase {
const timestamp = generateTimestamp();
contact_fields[getProtectedFieldName("SERVER_SYNC_LATEST")] = timestamp;

const auth_user_id = this.localStorageService.getProtected("AUTH_USER_ID") || null;

// TODO - get DTO from api (?)
const data = {
auth_user_id,
contact_fields,
app_version: _app_builder_version,
device_info: this.device_info,
app_deployment_name: name,
dynamic_data,
};

console.log("[SERVER] sync data", data);
return new Promise<string>((resolve, reject) => {
this.http
Expand Down

0 comments on commit 6af7ee8

Please sign in to comment.