Skip to content

Commit

Permalink
chore: add sentry account creation
Browse files Browse the repository at this point in the history
  • Loading branch information
LucasCharrier committed Nov 20, 2024
1 parent 959e2b7 commit a1ebed0
Show file tree
Hide file tree
Showing 7 changed files with 193 additions and 8 deletions.
15 changes: 8 additions & 7 deletions .talismanrc
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
fileignoreconfig:
- filename: .github/workflows/nodejs.yml
checksum: bf0ae31737daf27be68b7d2c4e63e1ef14cb799f1853462fdadcf2422ddd53a2
- filename: __tests__/test-worker.ts
checksum: 462f569a2625f2fdb0938f0abc109ed24f0241d9f5592ead16475e2efde0aa47
- filename: package-lock.json
checksum: add9168b63b1a9219558f2be2378591402a00d394be3bd9102715e43f8278e4f
- filename: src/app/(private)/(dashboard)/admin/signaux-faibles-beta/AdminProductClientPage.tsx
- filename: src/app/api/services/actions.ts
checksum: 9dfef4033112a7c878edfbb4cffe6bf7a888144aaf3e80646a2ee19b9f923b98
- filename: src/models/jobs/services.ts
checksum: ee599ef3117bbfd15bbf5cf7843e75a8177c3e03eb0311ebbe71a3228b19e280
- filename: src/server/queueing/workers/create-sentry-account.ts
checksum: 3f10e3c1996e2b05dfa5276d84542b19de5a9f4dd5282255980c300f5c526e30
version: ""
ux-faibles-beta/AdminProductClientPage.tsx
checksum: 9c740513497d70e871e4abcad3a9e509ef5b1a0da9db2d0d2d336c19854303b4
- filename: src/app/(private)/(dashboard)/incubators/[id]/info-form/page.tsx
checksum: 1ec9acf039a7d336e710ef7b47c385eb7080293ee2750c2cf99e9056cde7f129
Expand Down
21 changes: 21 additions & 0 deletions migrations/20241120171235_add_sentry_teams_table.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
exports.up = async function (knex) {
await knex.schema.createTable("sentry_teams", (table) => {
table.uuid("id").primary().defaultTo(knex.raw("gen_random_uuid()")); // UUID pour l'identifiant unique
table.string("sentry_id").notNullable().unique(); // Identifiant de team Sentry
table.uuid("startup_id").nullable(); // UUID lié à la table startups
table.string("name").notNullable(); // Nom de l'équipe

// Clé étrangère vers la table startups
table
.foreign("startup_id")
.references("uuid")
.inTable("startups")
.onDelete("CASCADE");

table.timestamps(true, true); // Champs created_at et updated_at
});
};

exports.down = async function (knex) {
await knex.schema.dropTableIfExists("sentry_teams");
};
56 changes: 56 additions & 0 deletions src/app/(private)/(dashboard)/services/sentry/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { redirect } from "next/navigation";
import { getServerSession } from "next-auth/next";

import AccountDetails from "@/components/Service/AccountDetails";
import SentryServiceForm from "@/components/Service/SentryServiceForm";
import { getServiceAccount } from "@/lib/kysely/queries/services";
import { sentryServiceInfoToModel } from "@/models/mapper/sentryMapper";
import { sentryUserSchemaType } from "@/models/sentry";
import { SERVICES } from "@/models/services";
import config from "@/server/config";
import { authOptions } from "@/utils/authoptions";

const buildLinkToSentryTeam = (
team: sentryUserSchemaType["metadata"]["teams"][0]
) => {
return team.name ? (
<a href={`${config.SENTRY_WEBSITE_URL}/${team.slug}`} target="_blank">
{team.name}
</a>
) : (
team.name
);
};

export default async function SentryPage() {
const session = await getServerSession(authOptions);
if (!session) {
redirect("/login");
}

const rawAccount = await getServiceAccount(
session.user.uuid,
SERVICES.SENTRY
);
const service_account = rawAccount
? sentryServiceInfoToModel(rawAccount)
: undefined;

return (
<>
<h1>Compte Sentry</h1>
{service_account ? (
<AccountDetails
account={service_account}
data={service_account.metadata.teams.map((team) => [
buildLinkToSentryTeam(team),
team.role,
])}
headers={["nom", "niveau d'accès"]}
/>
) : (
<SentryServiceForm />
)}
</>
);
}
43 changes: 42 additions & 1 deletion src/app/api/services/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,20 @@ import { match } from "ts-pattern";

import { db } from "@/lib/kysely";
import { getUserBasicInfo, getUserStartups } from "@/lib/kysely/queries/users";
import { matomoAccountRequestWrapperSchemaType } from "@/models/actions/service";
import {
matomoAccountRequestWrapperSchemaType,
sentryAccountRequestWrapperSchemaType,
} from "@/models/actions/service";
import {
CreateMattermostAccountDataSchema,
CreateMatomoAccountDataSchema,
CreateSentryAccountDataSchema,
} from "@/models/jobs/services";
import { ACCOUNT_SERVICE_STATUS, SERVICES } from "@/models/services";
import { encryptPassword } from "@/server/controllers/utils";
import { getBossClientInstance } from "@/server/queueing/client";
import { createMatomoServiceAccountTopic } from "@/server/queueing/workers/create-matomo-account";
import { createSentryServiceAccountTopic } from "@/server/queueing/workers/create-sentry-account";
import { authOptions } from "@/utils/authoptions";
import {
AuthorizationError,
Expand All @@ -29,6 +34,7 @@ export const askAccountCreationForService = withErrorHandling(
data,
}:
| matomoAccountRequestWrapperSchemaType
| sentryAccountRequestWrapperSchemaType
| { service: SERVICES.MATTERMOST; data: any }) => {
// create task
const session = await getServerSession(authOptions);
Expand Down Expand Up @@ -74,6 +80,41 @@ export const askAccountCreationForService = withErrorHandling(
})
.execute();
})
.with(SERVICES.SENTRY, async () => {
if (!user.primary_email) {
throw new ValidationError(
"Un email primaire est obligatoire"
);
}
await bossClient.send(
createSentryServiceAccountTopic,
CreateSentryAccountDataSchema.parse({
email: user.primary_email,
login: user.primary_email,
password: encryptPassword(
data.password ||
crypto
.randomBytes(20)
.toString("base64")
.slice(0, -2)
),
}),
{
retryLimit: 50,
retryBackoff: true,
}
);
await db
.insertInto("service_accounts")
.values({
user_id: user.uuid,
email: user.primary_email,
account_type: SERVICES.SENTRY,
status: ACCOUNT_SERVICE_STATUS.ACCOUNT_CREATION_PENDING,
})
.execute();
})

.otherwise(() => {
// otherwise or exhaustive should be defined otherwise function is not awaited
// cf https://github.com/gvergnaud/ts-pattern/issues/163
Expand Down
11 changes: 11 additions & 0 deletions src/lib/kysely/queries/services.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { db } from "@/lib/kysely";
import { SERVICES } from "@/models/services";

export async function getServiceAccount(userId: string, service: SERVICES) {
return db
.selectFrom("service_accounts")
.selectAll()
.where("user_id", "=", userId)
.where("account_type", "=", service)
.executeTakeFirst();
}
12 changes: 12 additions & 0 deletions src/models/jobs/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,15 @@ export const CreateMatomoAccountDataSchema =
export type CreateMatomoAccountDataSchemaType = z.infer<
typeof CreateMatomoAccountDataSchema
>;

export const CreateSentryAccountDataSchema =
MaintenanceWrapperDataSchema.extend({
email: z.string().email(), // Valide que l'email est bien formaté
login: z.string().min(1, "Le nom d'utilisateur est requis"), // Valide que le nom d'utilisateur n'est pas vide
password: z
.string()
.min(6, "Le mot de passe doit contenir au moins 6 caractères"), // Valide que le mot de passe contient au moins 6 caractères
}).strict();
export type CreateSentryAccountDataSchemaType = z.infer<
typeof CreateSentryAccountDataSchema
>;
43 changes: 43 additions & 0 deletions src/server/queueing/workers/create-sentry-account.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import pAll from "p-all";
import PgBoss from "pg-boss";

import { db } from "@/lib/kysely";
import { CreateSentryAccountDataSchemaType } from "@/models/jobs/services";
import { sentryMetadataToModel } from "@/models/mapper/sentryMapper";
import { ACCOUNT_SERVICE_STATUS, SERVICES } from "@/models/services";
import { sentryClient } from "@/server/config/sentry.config";
import { decryptPassword } from "@/server/controllers/utils";

export const createSentryServiceAccountTopic = "create-sentry-service-account";

export async function createSentryServiceAccount(
job: PgBoss.Job<CreateSentryAccountDataSchemaType>
) {
console.log(
`Create sentry service account for ${job.data.login}`,
job.id,
job.name
);

// throw new Error("Account could not be created");
const userLogin = job.data.email;

await sentryClient.createUser({
email: job.data.email,
password: decryptPassword(job.data.password),
userLogin,
alias: job.data.email,
teams: [],
});

const result = await db
.updateTable("service_accounts")
.set({
service_user_id: userLogin,
status: ACCOUNT_SERVICE_STATUS.ACCOUNT_FOUND,
metadata: JSON.stringify(metadata),
})
.executeTakeFirstOrThrow();

console.log(`the sentry account has been created for ${userLogin}`);
}

0 comments on commit a1ebed0

Please sign in to comment.