• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

inclusion-numerique / coop-mediation-numerique / a234bd3b-64e5-4f5f-b040-d9b185fdce4c

01 Apr 2026 12:25PM UTC coverage: 6.869% (-3.8%) from 10.692%
a234bd3b-64e5-4f5f-b040-d9b185fdce4c

Pull #468

circleci

marc-gavanier
feat(lieux-activite): extend visibility toggle to all mediateurs

- Rename cartography reference to "cartographie nationale des lieux
  d'inclusion numérique"
- Remove isConseillerNumerique condition for visibility toggle
- Simplify labels and add 24h update notice
Pull Request #468: feat(lieux-activite): extend visibility toggle to all mediateurs

470 of 10506 branches covered (4.47%)

Branch coverage included in aggregate %.

1355 of 16063 relevant lines covered (8.44%)

37.29 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/apps/web/src/api-client/apiClient.ts
1
import { prismaClient } from '@app/web/prismaClient'
2
import { generateRandomSecret } from '@app/web/security/generateRandomSecret'
3
import { hashSecret, verifySecret } from '@app/web/security/hashSecret'
4
import { ApiClient, ApiClientScope } from '@prisma/client'
5

6
export type CreateApiClientInput = {
7
  id?: string
8
  name: string
9
  scopes: ApiClientScope[]
10
  validUntil?: Date
11
  validFrom?: Date
12
}
13

14
export const createApiClient = async ({
×
15
  id,
16
  name,
17
  scopes,
18
  validFrom,
19
  validUntil,
20
}: CreateApiClientInput): Promise<
21
  ApiClient & {
22
    secret: string
23
  }
24
> => {
25
  const secret = generateRandomSecret()
×
26
  const secretHash = hashSecret(secret)
×
27

28
  const apiClient = await prismaClient.apiClient.create({
×
29
    data: {
30
      id,
31
      name,
32
      secretHash, // Store the hashed secret in the database
33
      validFrom: validFrom ?? new Date(),
×
34
      validUntil,
35
      scopes,
36
    },
37
  })
38

39
  return {
×
40
    ...apiClient,
41
    secret,
42
    secretHash,
43
  }
44
}
45

46
export type CreateApiClientOutput = Awaited<ReturnType<typeof createApiClient>>
47

48
export const rotateApiClientSecret = async ({
×
49
  clientId,
50
}: {
51
  clientId: string
52
}) => {
53
  const secret = generateRandomSecret()
×
54
  const secretHash = hashSecret(secret)
×
55

56
  await prismaClient.apiClient.update({
×
57
    where: { id: clientId },
58
    data: {
59
      secretHash, // Store the hashed secret in the database
60
      updated: new Date(),
61
    },
62
  })
63

64
  return {
×
65
    clientId,
66
    secret,
67
    secretHash,
68
  }
69
}
70

71
export const changeApiClientScopes = async ({
×
72
  clientId,
73
  scopes,
74
}: {
75
  clientId: string
76
  scopes: ApiClientScope[]
77
}) => {
78
  const apiClient = await prismaClient.apiClient.findUnique({
×
79
    where: { id: clientId },
80
  })
81

82
  if (!apiClient) {
×
83
    throw new Error('Api client not found')
×
84
  }
85

86
  await prismaClient.apiClient.update({
×
87
    where: { id: clientId },
88
    data: {
89
      scopes,
90
      updated: new Date(),
91
    },
92
  })
93
}
94

95
export const authenticateApiCient = async (
×
96
  clientId: string,
97
  clientSecret: string,
98
): Promise<ApiClient | null> => {
99
  // Ensure that clientId is a uuid v4 using regex
100
  if (!/^[\da-f]{8}(?:-[\da-f]{4}){3}-[\da-f]{12}$/.test(clientId)) return null
×
101

102
  const apiClient = await prismaClient.apiClient.findUnique({
×
103
    where: { id: clientId },
104
  })
105

106
  if (!apiClient) return null
×
107

108
  const isValid = verifySecret(clientSecret, apiClient.secretHash)
×
109

110
  if (!isValid) return null
×
111

112
  const now = new Date()
×
113

114
  if (apiClient.validFrom > now) return null
×
115

116
  if (!!apiClient.validUntil && apiClient.validUntil < now) {
×
117
    return null
×
118
  }
119

120
  return apiClient
×
121
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc