Skip to content

Commit

Permalink
feat: track logins on supabase add infra related details to the balle…
Browse files Browse the repository at this point in the history
…rine
  • Loading branch information
MatanYadaev authored and pratapalakshmi committed Nov 3, 2024
1 parent 5b958d6 commit 10961bd
Show file tree
Hide file tree
Showing 10 changed files with 2,258 additions and 562 deletions.
4 changes: 4 additions & 0 deletions deploy/.env
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,7 @@ WEBSOCKET_SVC_PORT=3500
KYB_APP_PORT=5201
BACKOFFICE_PORT=5137
HEADLESS_SVC_PORT=5173
DOMAIN_NAME=""
TELEMETRY_ENABLED=true
TELEMETRY_SUPABASE_URL=""
TELEMETRY_SUPABASE_API_KEY=""
3 changes: 3 additions & 0 deletions deploy/docker-compose-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ services:
HASHING_KEY_SECRET: ${HASHING_KEY_SECRET}
HASHING_KEY_SECRET_BASE64: ${HASHING_KEY_SECRET_BASE64}
NOTION_API_KEY: ${NOTION_API_KEY}
TELEMETRY_ENABLED: ${TELEMETRY_ENABLED}
TELEMETRY_SUPABASE_URL: ${TELEMETRY_SUPABASE_URL}
TELEMETRY_SUPABASE_API_KEY: ${TELEMETRY_SUPABASE_API_KEY}
depends_on:
ballerine-postgres:
condition: service_healthy
Expand Down
2,616 changes: 2,057 additions & 559 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions services/workflows-service/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,6 @@ WEB_UI_SDK_URL=http://localhost:5202
#HASHING_KEY_SECRET="$2b$10$FovZTB91/QQ4Yu28nvL8e."
HASHING_KEY_SECRET_BASE64=JDJiJDEwJDNFeWtwWEs4QkdiczlRaWFwLkM4Vk8=
NOTION_API_KEY=secret
TELEMETRY_ENABLED=true
TELEMETRY_SUPABASE_URL=""
TELEMETRY_SUPABASE_API_KEY=""
3 changes: 2 additions & 1 deletion services/workflows-service/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,9 @@ COPY --from=dev /app/scripts ./scripts
COPY --from=dev /app/src ./src
COPY --from=dev /app/tsconfig.build.json ./tsconfig.build.json
COPY --from=dev /app/tsconfig.json ./tsconfig.json
COPY --from=dev /app/entrypoint.sh ./entrypoint.sh

EXPOSE 3000

CMD [ "dumb-init", "npm", "run", "prod" ]
ENTRYPOINT ["/app/entrypoint.sh"]

71 changes: 71 additions & 0 deletions services/workflows-service/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#!/usr/bin/env bash

set -e

infra_file="/tmp/infra.json"

## Get cloudProvider details
function get_cloud_provider() {
release_details=$(uname -r)
if [[ $release_details == *"amzn"* ]];then
# Example: 5.10.192-183.736.amzn2.x86_64
cloud_provider="amazon";
elif [[ $release_details == *"azure"* ]];then
# Example: 5.15.0-1059-azure
cloud_provider="azure";
elif [[ $release_details == *"cloud"* ]];then
# Example: 6.1.0-18-cloud-amd64
cloud_provider="gcp";
elif [[ $release_details == *"generic"* ]];then
# Example: 6.8.0-31-generic
cloud_provider="digitalocean"
elif [[ $release_details == *"ecs"* ]];then
cloud_provider="alibaba"
elif [[ -n "${DYNO}" ]];then
cloud_provider="heroku"
else
cloud_provider="others(including local)";
fi
}

## Get deployment tool details
function get_tool() {
if [[ -z "${KUBERNETES_SERVICE_HOST}" ]]; then
dep_tool="likely docker";
else
dep_tool="kubernetes";
fi
}


## Check hostname
function get_hostname() {
hostname="$(cat /etc/hostname)"
}

## Get current Time
function get_current_time(){
currentTime="$(date -u -Iseconds)"
}

## Check if it's a ECS Fargate deployment
function check_for_fargate() {
if [[ $cloud_provider == "amazon" && $dep_tool == "likely docker" ]]; then
dep_tool="ecs-fargate"
fi
}

## Main Block
get_cloud_provider
get_tool
get_hostname
check_for_fargate
get_current_time

infra_json='{"cloudProvider":"'"$cloud_provider"'","tool":"'"$dep_tool"'","hostname":"'"$hostname"'", "currentTime": "'"$currentTime"'"}'
echo "$infra_json"

echo $infra_json > $infra_file

dumb-init npm run prod

1 change: 1 addition & 0 deletions services/workflows-service/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
"@sentry/integrations": "^7.52.1",
"@sentry/node": "^7.52.1",
"@sinclair/typebox": "0.32.15",
"@supabase/supabase-js": "^2.43.1",
"@t3-oss/env-core": "^0.6.1",
"ajv": "^8.12.0",
"ajv-formats": "^2.1.1",
Expand Down
24 changes: 22 additions & 2 deletions services/workflows-service/src/auth/local/local-auth.guard.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,34 @@
import { AuthGuard } from '@nestjs/passport';
import { ExecutionContext } from '@nestjs/common';
import type { Request } from 'express';
import { createClient } from '@supabase/supabase-js';
import { env } from '@/env';

export class LocalAuthGuard extends AuthGuard('local') {
async canActivate(context: ExecutionContext) {
const result = await super.canActivate(context);
const request = context.switchToHttp().getRequest<Request>();

await super.logIn(request);

if (env.TELEMETRY_ENABLED && env.TELEMETRY_SUPABASE_URL && env.TELEMETRY_SUPABASE_API_KEY) {
try{
const SupabaseClient = createClient(
env.TELEMETRY_SUPABASE_URL,
env.TELEMETRY_SUPABASE_API_KEY,
{
db: { schema: 'public' },
},
);
const fullUrl = `${request.protocol}://${request.get('Host')}${request.originalUrl}`;
const { data: result, error } = await SupabaseClient.from('logins').insert([{ url: fullUrl }]);
if(error) {
console.error('Error inserting data:', error.message);
return;
}
}
catch(err){
console.error('Unexpected error:', err);
}
}
return result as boolean;
}
}
72 changes: 72 additions & 0 deletions services/workflows-service/src/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,79 @@ export const env = createEnv({
* clientPrefix is required.
*/
clientPrefix: 'PUBLIC_',
<<<<<<< HEAD
server: serverEnvSchema,
=======
server: {
LOG_LEVEL: z.enum(['debug', 'info', 'warn', 'error']).default('info'),
NODE_ENV: z.enum(['development', 'production', 'test', 'local']), // TODO: remove 'test', 'local'
ENVIRONMENT_NAME: z.enum(['development', 'sandbox', 'production', 'staging', 'test', 'local']),
ENV_FILE_NAME: z.string().optional(),
BCRYPT_SALT: z.coerce.number().int().nonnegative().or(z.string()),
PORT: z.coerce.number(),
DB_URL: z.string().url(),
SESSION_SECRET: z.string(),
HASHING_KEY_SECRET: z.string(),
SESSION_EXPIRATION_IN_MINUTES: z.coerce.number().nonnegative().gt(0).default(60),
BACKOFFICE_CORS_ORIGIN: z.string().transform(urlArrayTransformer),
WORKFLOW_DASHBOARD_CORS_ORIGIN: z.string().transform(urlArrayTransformer),
KYB_EXAMPLE_CORS_ORIGIN: z.string().transform(urlArrayTransformer),
KYC_EXAMPLE_CORS_ORIGIN: z
.string()
.optional()
.transform(value => {
if (value === undefined) {
return value;
}

return urlArrayTransformer(value);
}),
AWS_S3_BUCKET_NAME: z.string().optional(),
AWS_S3_BUCKET_KEY: z.string().optional(),
AWS_S3_BUCKET_SECRET: z.string().optional(),
API_KEY: z.string(),
SENTRY_DSN: z.string().nullable().optional(),
RELEASE: z.string().nullable().optional(),
ADMIN_API_KEY: z.string().optional(),
MAIL_ADAPTER: z
.enum(['sendgrid', 'log'])
.default('sendgrid')
.describe(
`Which mail adapter to use. Use "log" during development to log emails to the console. In production, use "sendgrid" to send emails via SendGrid.`,
),
UNIFIED_API_URL: z.string().url().describe('The URL of the Unified API.'),
UNIFIED_API_TOKEN: z
.string()
.optional()
.describe(
'API token for the Unified API. Used for authenticating outgoing requests to the Unified API.',
),
UNIFIED_API_SHARED_SECRET: z
.string()
.optional()
.describe('Shared secret for the Unified API. Used for verifying incoming callbacks.'),
SALESFORCE_API_VERSION: z
.string()
.optional()
.default('58.0')
.describe('Salesforce API version'),
SALESFORCE_CONSUMER_KEY: z.string().optional().describe('Salesforce consumer key'),
SALESFORCE_CONSUMER_SECRET: z.string().optional().describe('Salesforce consumer secret'),
APP_API_URL: z.string().url().describe('The URL of the workflows-service API'),
COLLECTION_FLOW_URL: z.string().url().optional().describe('The URL of the Collection Flow App'),
WEB_UI_SDK_URL: z.string().url().optional().describe('The URL of the Web UI SDK App'),
DATA_MIGRATION_BUCKET_NAME: z
.string()
.optional()
.describe('Bucket name of Data migration folders'),
TELEMETRY_ENABLED: z
.enum(['true', 'false'])
.default('true')
.transform(value => value === 'true')
.describe('Enable or disable telemetry'),
TELEMETRY_SUPABASE_URL: z.string().url().optional().describe('Supabase URL for telemetry'),
TELEMETRY_SUPABASE_API_KEY: z.string().optional().describe('Supabase API key for telemetry'),
},
client: {},
/**
* What object holds the environment variables at runtime.
Expand Down
23 changes: 23 additions & 0 deletions services/workflows-service/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { AppLoggerService } from './common/app-logger/app-logger.service';
import { exceptionValidationFactory } from './errors';
import swagger from '@/swagger/swagger';
import { applyFormats, patchNestJsSwagger } from 'ballerine-nestjs-typebox';
import { createClient } from '@supabase/supabase-js';

// provide swagger OpenAPI generator support
patchNestJsSwagger();
Expand Down Expand Up @@ -50,6 +51,28 @@ const corsOrigins = [
];

const main = async () => {
// Infra related data
const infradata = require('/tmp/infra.json');
if (env.TELEMETRY_ENABLED && env.TELEMETRY_SUPABASE_URL && env.TELEMETRY_SUPABASE_API_KEY) {
try {
const SupabaseClient = createClient(
env.TELEMETRY_SUPABASE_URL,
env.TELEMETRY_SUPABASE_API_KEY,
{
db: { schema: 'public' },
},
);
const { data, error } = await SupabaseClient.from('infra').insert([infradata]);
if (error) {
console.error('Error inserting data:', error.message);
} else {
console.log('Data inserted successfully:', data);
}
} catch (error) {
console.error('Error inserting data:', error.message);
}
}

const app = await NestFactory.create(AppModule, {
bufferLogs: true, //will be buffered until a custom logger is attached
snapshot: true,
Expand Down

0 comments on commit 10961bd

Please sign in to comment.