prow-authority 3.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (168) hide show
  1. package/.claude/settings.local.json +11 -0
  2. package/.dockerignore +11 -0
  3. package/.github/workflows/_deploy.yml +191 -0
  4. package/.github/workflows/deploy-dev.yml +30 -0
  5. package/.github/workflows/deploy-prod.yml +34 -0
  6. package/CLAUDE.md +114 -0
  7. package/Dockerfile +61 -0
  8. package/Dockerfile.dev +24 -0
  9. package/README.md +1 -0
  10. package/package.json +61 -0
  11. package/src/App.ts +40 -0
  12. package/src/helpers/api-error.ts +21 -0
  13. package/src/helpers/authority.ts +216 -0
  14. package/src/helpers/config.ts +4 -0
  15. package/src/helpers/holded.ts +105 -0
  16. package/src/helpers/mysql.ts +15 -0
  17. package/src/helpers/notifications.ts +55 -0
  18. package/src/helpers/random.ts +18 -0
  19. package/src/helpers/s3.ts +43 -0
  20. package/src/helpers/stripe.ts +8 -0
  21. package/src/index.ts +47 -0
  22. package/src/v1/common-types.ts +12 -0
  23. package/src/v1/controllers/admin/accounting-seat-controller.ts +148 -0
  24. package/src/v1/controllers/admin/billing-cycle-controller.ts +171 -0
  25. package/src/v1/controllers/admin/invoice-controller.ts +222 -0
  26. package/src/v1/controllers/admin/license-controller.ts +190 -0
  27. package/src/v1/controllers/admin/license-limit-controller.ts +160 -0
  28. package/src/v1/controllers/admin/notification-controller.ts +115 -0
  29. package/src/v1/controllers/admin/organization-controller.ts +210 -0
  30. package/src/v1/controllers/admin/organization-license-controller.ts +224 -0
  31. package/src/v1/controllers/admin/organization-license-limit-controller.ts +196 -0
  32. package/src/v1/controllers/admin/organization-usage-controller.ts +167 -0
  33. package/src/v1/controllers/admin/payment-method-controller.ts +173 -0
  34. package/src/v1/controllers/admin/policy-controller.ts +138 -0
  35. package/src/v1/controllers/admin/policy-version-controller.ts +143 -0
  36. package/src/v1/controllers/admin/schedule-controller.ts +181 -0
  37. package/src/v1/controllers/admin/service-controller.ts +206 -0
  38. package/src/v1/controllers/admin/service-notification-controller.ts +166 -0
  39. package/src/v1/controllers/admin/user-controller.ts +282 -0
  40. package/src/v1/controllers/admin/user-organization-controller.ts +172 -0
  41. package/src/v1/controllers/admin/user-policy-controller.ts +164 -0
  42. package/src/v1/controllers/auth/@deprecated oauth-controller.ts +191 -0
  43. package/src/v1/controllers/auth/identity-token-controller.ts +286 -0
  44. package/src/v1/controllers/auth/organization-token-controller.ts +277 -0
  45. package/src/v1/controllers/auth/temp-code-controller.ts +60 -0
  46. package/src/v1/controllers/billing/billing-controller.ts +805 -0
  47. package/src/v1/controllers/organizations/public-organization-controller.ts +1796 -0
  48. package/src/v1/controllers/policies/public-policy-controller.ts +68 -0
  49. package/src/v1/controllers/services/public-service-controller.ts +177 -0
  50. package/src/v1/controllers/services/service-controller.ts +474 -0
  51. package/src/v1/controllers/users/public-user-controller.ts +471 -0
  52. package/src/v1/daos/@deprecated auth-dao.ts +173 -0
  53. package/src/v1/daos/accounting-seat-dao.ts +111 -0
  54. package/src/v1/daos/billing-cycle-dao.ts +107 -0
  55. package/src/v1/daos/invoice-dao.ts +131 -0
  56. package/src/v1/daos/license-dao.ts +152 -0
  57. package/src/v1/daos/license-limit-dao.ts +120 -0
  58. package/src/v1/daos/organization-dao.ts +109 -0
  59. package/src/v1/daos/organization-license-dao.ts +117 -0
  60. package/src/v1/daos/organization-license-limit-dao.ts +157 -0
  61. package/src/v1/daos/organization-usage-dao.ts +115 -0
  62. package/src/v1/daos/payment-method-dao.ts +110 -0
  63. package/src/v1/daos/policy-dao.ts +105 -0
  64. package/src/v1/daos/policy-version-dao.ts +112 -0
  65. package/src/v1/daos/redis-dao.ts +38 -0
  66. package/src/v1/daos/schema.sql +462 -0
  67. package/src/v1/daos/service-dao.ts +118 -0
  68. package/src/v1/daos/service-notification-dao.ts +120 -0
  69. package/src/v1/daos/user-dao.ts +127 -0
  70. package/src/v1/daos/user-organization-dao.ts +123 -0
  71. package/src/v1/daos/user-policy-dao.ts +109 -0
  72. package/src/v1/middlewares/auth/identity-token-middleware.ts +51 -0
  73. package/src/v1/middlewares/auth/organization-token-middleware.ts +73 -0
  74. package/src/v1/middlewares/auth/service-token-middleware.ts +167 -0
  75. package/src/v1/middlewares/authority-middleware.ts +97 -0
  76. package/src/v1/middlewares/organizations/organization-middleware.ts +94 -0
  77. package/src/v1/middlewares/static/static-middleware.ts +41 -0
  78. package/src/v1/middlewares/users/user-middleware.ts +41 -0
  79. package/src/v1/routes/admin/accounting-seat-router.ts +89 -0
  80. package/src/v1/routes/admin/admin-router.ts +61 -0
  81. package/src/v1/routes/admin/billing-cycle-router.ts +89 -0
  82. package/src/v1/routes/admin/invoice-router.ts +89 -0
  83. package/src/v1/routes/admin/license-limit-router.ts +89 -0
  84. package/src/v1/routes/admin/license-router.ts +89 -0
  85. package/src/v1/routes/admin/notification-router.ts +61 -0
  86. package/src/v1/routes/admin/organization-license-limit-router.ts +89 -0
  87. package/src/v1/routes/admin/organization-license-router.ts +89 -0
  88. package/src/v1/routes/admin/organization-router.ts +89 -0
  89. package/src/v1/routes/admin/payment-method-router.ts +89 -0
  90. package/src/v1/routes/admin/policy-router.ts +89 -0
  91. package/src/v1/routes/admin/policy-version-router.ts +89 -0
  92. package/src/v1/routes/admin/schedule-router.ts +75 -0
  93. package/src/v1/routes/admin/service-notification-router.ts +98 -0
  94. package/src/v1/routes/admin/service-router.ts +89 -0
  95. package/src/v1/routes/admin/user-organization-router.ts +89 -0
  96. package/src/v1/routes/admin/user-policy-router.ts +89 -0
  97. package/src/v1/routes/admin/user-router.ts +89 -0
  98. package/src/v1/routes/auth/@deprecated auth-router.ts +199 -0
  99. package/src/v1/routes/auth/identity-token-router.ts +147 -0
  100. package/src/v1/routes/auth/organization-token-router.ts +70 -0
  101. package/src/v1/routes/billing/billing-router.ts +227 -0
  102. package/src/v1/routes/organizations/invoices-router.ts +48 -0
  103. package/src/v1/routes/organizations/payments-router.ts +104 -0
  104. package/src/v1/routes/organizations/public-organization-router.ts +319 -0
  105. package/src/v1/routes/organizations/users-router.ts +139 -0
  106. package/src/v1/routes/policies/public-policy-router.ts +50 -0
  107. package/src/v1/routes/services/public-service-router.ts +89 -0
  108. package/src/v1/routes/services/service-router.ts +197 -0
  109. package/src/v1/routes/users/public-user-router.ts +230 -0
  110. package/src/v1/routes/v1-router.ts +50 -0
  111. package/src/v1/schemas/admin/accounting-seat-schemas.ts +22 -0
  112. package/src/v1/schemas/admin/billing-cycle-schemas.ts +20 -0
  113. package/src/v1/schemas/admin/invoice-schemas.ts +41 -0
  114. package/src/v1/schemas/admin/license-limit-schemas.ts +25 -0
  115. package/src/v1/schemas/admin/license-schemas.ts +30 -0
  116. package/src/v1/schemas/admin/organization-license-limit-schemas.ts +29 -0
  117. package/src/v1/schemas/admin/organization-license-schemas.ts +43 -0
  118. package/src/v1/schemas/admin/organization-schemas.ts +67 -0
  119. package/src/v1/schemas/admin/organization-usage-schemas.ts +15 -0
  120. package/src/v1/schemas/admin/payment-method-schemas.ts +40 -0
  121. package/src/v1/schemas/admin/policy-schemas.ts +31 -0
  122. package/src/v1/schemas/admin/service-notification-schemas.ts +23 -0
  123. package/src/v1/schemas/admin/service-schemas.ts +56 -0
  124. package/src/v1/schemas/admin/user-organization-schemas.ts +24 -0
  125. package/src/v1/schemas/admin/user-policy-schemas.ts +17 -0
  126. package/src/v1/schemas/admin/user-schemas.ts +52 -0
  127. package/src/v1/schemas/auth/auth-schemas.ts +55 -0
  128. package/src/v1/schemas/billing/billing-schemas.ts +32 -0
  129. package/src/v1/schemas/organizations/public-organization-schemas.ts +77 -0
  130. package/src/v1/schemas/payments/payment-method-schemas.ts +0 -0
  131. package/src/v1/schemas/services/public-service-schemas.ts +5 -0
  132. package/src/v1/schemas/services/service-schemas.ts +27 -0
  133. package/src/v1/schemas/users/public-user-schemas.ts +73 -0
  134. package/src/v1/types/admin/accounting-seat-types.ts +16 -0
  135. package/src/v1/types/admin/billing-cycle-types.ts +14 -0
  136. package/src/v1/types/admin/invoice-types.ts +39 -0
  137. package/src/v1/types/admin/license-limit-types.ts +16 -0
  138. package/src/v1/types/admin/license-types.ts +29 -0
  139. package/src/v1/types/admin/organization-license-limit-types.ts +13 -0
  140. package/src/v1/types/admin/organization-license-types.ts +21 -0
  141. package/src/v1/types/admin/organization-types.ts +60 -0
  142. package/src/v1/types/admin/organization-usage-types.ts +17 -0
  143. package/src/v1/types/admin/payment-method-types.ts +30 -0
  144. package/src/v1/types/admin/policy-types.ts +9 -0
  145. package/src/v1/types/admin/policy-version-types.ts +12 -0
  146. package/src/v1/types/admin/service-notification-types.ts +20 -0
  147. package/src/v1/types/admin/service-types.ts +36 -0
  148. package/src/v1/types/admin/user-organization-types.ts +31 -0
  149. package/src/v1/types/admin/user-types.ts +44 -0
  150. package/src/v1/types/auth/@deprecated auth-types.ts +26 -0
  151. package/src/v1/types/auth/common-types.ts +7 -0
  152. package/src/v1/types/auth/confirmation-types.ts +5 -0
  153. package/src/v1/types/auth/identity-token-types.ts +8 -0
  154. package/src/v1/types/auth/organization-token-types.ts +23 -0
  155. package/src/v1/types/auth/otp-types.ts +12 -0
  156. package/src/v1/types/billing/billing-types.ts +37 -0
  157. package/src/v1/types/billing/ue-types.ts +29 -0
  158. package/src/v1/types/organizations/public-organization-types.ts +76 -0
  159. package/src/v1/types/policies/public-policy-types.ts +7 -0
  160. package/src/v1/types/services/public-service-types.ts +20 -0
  161. package/src/v1/types/services/service-types.ts +43 -0
  162. package/src/v1/types/users/public-user-types.ts +15 -0
  163. package/src/v1/types/users/user-policy-types.ts +11 -0
  164. package/static/error.html +55 -0
  165. package/static/grant.html +117 -0
  166. package/static/login.html +54 -0
  167. package/static/oauth-client-logo.webp +0 -0
  168. package/tsconfig.json +20 -0
@@ -0,0 +1,11 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(xargs ls -la)",
5
+ "Read(//home/beseif/Documentos/Back/prow-feu/**)",
6
+ "Read(//home/beseif/Documentos/Back/prow-feu/src/**)",
7
+ "Bash(find /home/beseif/Documentos -name \"prow-notifications.yaml\" 2>/dev/null)",
8
+ "Read(//home/beseif/Documentos/**)"
9
+ ]
10
+ }
11
+ }
package/.dockerignore ADDED
@@ -0,0 +1,11 @@
1
+ node_modules
2
+ dist
3
+ config
4
+ .git
5
+ .github
6
+ *.log
7
+ logs
8
+ .vscode
9
+ Dockerfile
10
+ Dockerfile.dev
11
+ .dockerignore
@@ -0,0 +1,191 @@
1
+ name: _deploy (reusable)
2
+
3
+ # Workflow reutilizable: build de imagen Docker + push a Amazon ECR.
4
+ #
5
+ # config/config.yaml sigue gestionandose exactamente igual que hoy: vive en
6
+ # el checkout de prow-configuration-files en la propia EC2 (mismo mecanismo
7
+ # de actualizacion que ya usa ese repo). Antes se conectaba con el proceso
8
+ # PM2 via symlink; con Docker, al tener el contenedor su propio filesystem
9
+ # aislado, un symlink que apunte fuera del mount no se resolveria dentro del
10
+ # contenedor. Por eso aqui se monta directamente el fichero real del
11
+ # checkout como bind-mount de solo lectura en el `docker run` (ver
12
+ # CONFIG_HOST_PATH). Ningun secreto de config pasa por GitHub Actions ni
13
+ # por AWS: no hace falta tocar IAM para eso.
14
+ #
15
+ # Variables de REPOSITORIO (mismo valor para dev y prod, no van en el
16
+ # Environment — misma cuenta/region de AWS para ambos entornos):
17
+ # ECR_REGISTRY — ej: 123456789.dkr.ecr.eu-west-1.amazonaws.com
18
+ # ECR_REPOSITORY — ej: prow/prow-authority
19
+ # AWS_REGION — ej: eu-west-1
20
+ # CONFIG_HOST_PATH — ruta al config.yaml real dentro del checkout de
21
+ # prow-configuration-files: /node/configuration-files/prow-authority.yaml
22
+ # Es la misma ruta en ambas EC2 — lo que cambia es la
23
+ # rama (develop/prod) checkouteada en cada maquina en
24
+ # esa misma ruta. (fichero, no carpeta — se monta 1:1
25
+ # sobre /app/config/config.yaml)
26
+ # Si algun dia dev/prod necesitan un valor distinto para alguna de estas
27
+ # (ej. prod en otra cuenta de AWS), basta con redefinirla tambien en el
28
+ # Environment correspondiente — una variable de Environment con el mismo
29
+ # nombre tiene prioridad sobre la de repositorio.
30
+ #
31
+ # Variables requeridas por GitHub Environment (dev/prod):
32
+ # INSTANCE_ID — id de la EC2 destino (i-xxxxxxxx)
33
+ # HOST_PORT — puerto del host mapeado al 5001 del contenedor
34
+ #
35
+ # Secrets requeridos (repositorio, ver _deploy.yml de prow-feu — misma cuenta AWS):
36
+ # AWS_ROLE_ARN — rol OIDC de CI con permisos ECR push + SSM SendCommand
37
+ # NPM_TOKEN — npm automation token para @beseif-solutions/*
38
+ #
39
+ # El llamador debe pasar `secrets: inherit`.
40
+
41
+ on:
42
+ workflow_call:
43
+ inputs:
44
+ environment:
45
+ description: GitHub Environment del que se leen secrets y variables (dev/prod)
46
+ required: true
47
+ type: string
48
+ image_tag:
49
+ description: >
50
+ Tag principal de la imagen. Dev usa "dev"; prod usa el tag de release (ej: "v1.2.3").
51
+ required: true
52
+ type: string
53
+ tag_latest:
54
+ description: Si true, publica tambien la etiqueta :latest (solo para prod).
55
+ required: false
56
+ type: boolean
57
+ default: false
58
+
59
+ permissions:
60
+ contents: read
61
+ id-token: write # requerido para OIDC (sts:AssumeRoleWithWebIdentity)
62
+
63
+ jobs:
64
+ build-and-push:
65
+ runs-on: ubuntu-latest
66
+ environment: ${{ inputs.environment }}
67
+
68
+ env:
69
+ ECR_REGISTRY: ${{ vars.ECR_REGISTRY }}
70
+ ECR_REPOSITORY: ${{ vars.ECR_REPOSITORY }}
71
+
72
+ steps:
73
+ - name: Checkout
74
+ uses: actions/checkout@v4
75
+
76
+ - name: Set up Docker Buildx
77
+ uses: docker/setup-buildx-action@v3
78
+
79
+ - name: Configure AWS credentials (OIDC)
80
+ uses: aws-actions/configure-aws-credentials@v4
81
+ with:
82
+ role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
83
+ aws-region: ${{ vars.AWS_REGION }}
84
+
85
+ - name: Login to Amazon ECR
86
+ uses: aws-actions/amazon-ecr-login@v2
87
+
88
+ # Construye la lista de tags segun el entorno:
89
+ # dev → :dev
90
+ # prod → :v1.2.3 + :latest
91
+ - name: Compute image tags
92
+ id: tags
93
+ run: |
94
+ BASE="${ECR_REGISTRY}/${ECR_REPOSITORY}"
95
+ TAGS="${BASE}:${{ inputs.image_tag }}"
96
+ if [ "${{ inputs.tag_latest }}" = "true" ]; then
97
+ TAGS="${TAGS}"$'\n'"${BASE}:latest"
98
+ fi
99
+ echo "tags<<EOF" >> "$GITHUB_OUTPUT"
100
+ echo "${TAGS}" >> "$GITHUB_OUTPUT"
101
+ echo "EOF" >> "$GITHUB_OUTPUT"
102
+
103
+ - name: Write build secrets to files
104
+ run: printf '%s' "${NPM_TOKEN}" > /tmp/npm_token
105
+ env:
106
+ NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
107
+
108
+ - name: Build and push
109
+ uses: docker/build-push-action@v6
110
+ with:
111
+ context: .
112
+ push: true
113
+ tags: ${{ steps.tags.outputs.tags }}
114
+ secret-files: |
115
+ npm_token=/tmp/npm_token
116
+ cache-from: type=gha
117
+ cache-to: type=gha,mode=max
118
+
119
+ # Ordena a la EC2 (por instance ID) que haga login en ECR, descargue la
120
+ # imagen nueva y reinicie el contenedor montando el config.yaml real
121
+ # del checkout de prow-configuration-files (CONFIG_HOST_PATH) como
122
+ # solo lectura. El instance profile de la EC2 ya tiene permisos ECR
123
+ # pull, no hace falta nada nuevo en IAM.
124
+ - name: Deploy to EC2 (SSM)
125
+ run: |
126
+ set -euo pipefail
127
+
128
+ BASE="${ECR_REGISTRY}/${ECR_REPOSITORY}"
129
+ IMAGE="${BASE}:${{ inputs.image_tag }}"
130
+ CONTAINER=$(basename "${ECR_REPOSITORY}")
131
+
132
+ REMOTE="
133
+ aws ecr get-login-password --region ${{ vars.AWS_REGION }} \
134
+ | docker login --username AWS --password-stdin ${ECR_REGISTRY} \
135
+ && docker pull ${IMAGE} \
136
+ && docker stop ${CONTAINER} || true \
137
+ && docker rm ${CONTAINER} || true \
138
+ && docker run -d \
139
+ --name ${CONTAINER} \
140
+ --restart unless-stopped \
141
+ -p ${{ vars.HOST_PORT }}:5001 \
142
+ -v ${{ vars.CONFIG_HOST_PATH }}:/app/config/config.yaml:ro \
143
+ ${IMAGE}
144
+ "
145
+ B64=$(printf '%s' "${REMOTE}" | base64 -w0)
146
+
147
+ CMD_ID=$(aws ssm send-command \
148
+ --region ${{ vars.AWS_REGION }} \
149
+ --document-name AWS-RunShellScript \
150
+ --comment "deploy ${GITHUB_SHA::7} -> ${CONTAINER}:${{ inputs.image_tag }}" \
151
+ --targets "Key=instanceids,Values=${{ vars.INSTANCE_ID }}" \
152
+ --parameters "commands=[\"echo ${B64} | base64 -d | bash\"]" \
153
+ --query Command.CommandId --output text)
154
+ echo "SSM CommandId: ${CMD_ID}"
155
+
156
+ for _ in $(seq 1 60); do
157
+ STATUS=$(aws ssm list-command-invocations \
158
+ --region ${{ vars.AWS_REGION }} \
159
+ --command-id "${CMD_ID}" \
160
+ --query "CommandInvocations[0].Status" --output text 2>/dev/null || echo "Pending")
161
+ echo "Estado: ${STATUS}"
162
+ if echo "${STATUS}" | grep -qE 'Pending|InProgress|Delayed'; then
163
+ sleep 5; continue
164
+ fi
165
+ break
166
+ done
167
+
168
+ if [ "${STATUS}" != "Success" ]; then
169
+ echo "::error::Deploy fallido con estado: ${STATUS}"
170
+ aws ssm get-command-invocation \
171
+ --region ${{ vars.AWS_REGION }} \
172
+ --command-id "${CMD_ID}" \
173
+ --instance-id "${{ vars.INSTANCE_ID }}" \
174
+ --query "StandardErrorContent" --output text || true
175
+ exit 1
176
+ fi
177
+ echo "Deploy OK — ${CONTAINER}:${{ inputs.image_tag }}" >> "$GITHUB_STEP_SUMMARY"
178
+
179
+ - name: Summary
180
+ run: |
181
+ BASE="${ECR_REGISTRY}/${ECR_REPOSITORY}"
182
+
183
+ echo "### Imagen publicada y desplegada" >> "$GITHUB_STEP_SUMMARY"
184
+ echo "" >> "$GITHUB_STEP_SUMMARY"
185
+ echo "**Tags publicados:**" >> "$GITHUB_STEP_SUMMARY"
186
+ echo '```' >> "$GITHUB_STEP_SUMMARY"
187
+ echo "${BASE}:${{ inputs.image_tag }}" >> "$GITHUB_STEP_SUMMARY"
188
+ if [ "${{ inputs.tag_latest }}" = "true" ]; then
189
+ echo "${BASE}:latest" >> "$GITHUB_STEP_SUMMARY"
190
+ fi
191
+ echo '```' >> "$GITHUB_STEP_SUMMARY"
@@ -0,0 +1,30 @@
1
+ name: Deploy Dev
2
+
3
+ # Publica la imagen Docker del entorno Dev en ECR con tag :dev y despliega
4
+ # en la EC2 de dev via SSM.
5
+ #
6
+ # Tags que genera:
7
+ # :dev — siempre apunta al ultimo build de develop
8
+
9
+ on:
10
+ push:
11
+ branches: [develop]
12
+ workflow_dispatch: {}
13
+
14
+ # Cancela despliegues solapados de Dev (un push nuevo invalida el anterior).
15
+ concurrency:
16
+ group: deploy-dev
17
+ cancel-in-progress: true
18
+
19
+ permissions:
20
+ contents: read
21
+ id-token: write
22
+
23
+ jobs:
24
+ deploy:
25
+ uses: ./.github/workflows/_deploy.yml
26
+ with:
27
+ environment: dev
28
+ image_tag: dev
29
+ tag_latest: false
30
+ secrets: inherit
@@ -0,0 +1,34 @@
1
+ name: Deploy Prod
2
+
3
+ # Publica la imagen Docker del entorno Prod en ECR con el tag de release y
4
+ # despliega en la EC2 de prod via SSM.
5
+ # Se dispara al publicar un GitHub Release (tag v*) o manualmente.
6
+ # La aprobacion previa la impone la proteccion del environment `prod`
7
+ # (required reviewers).
8
+ #
9
+ # Tags que genera:
10
+ # :v1.2.3 — version exacta del release
11
+ # :latest — apunta siempre al ultimo release de produccion
12
+
13
+ on:
14
+ release:
15
+ types: [published]
16
+ workflow_dispatch: {}
17
+
18
+ # En Prod NO cancelamos un despliegue en curso.
19
+ concurrency:
20
+ group: deploy-prod
21
+ cancel-in-progress: false
22
+
23
+ permissions:
24
+ contents: read
25
+ id-token: write
26
+
27
+ jobs:
28
+ deploy:
29
+ uses: ./.github/workflows/_deploy.yml
30
+ with:
31
+ environment: prod
32
+ image_tag: ${{ github.ref_name }}
33
+ tag_latest: true
34
+ secrets: inherit
package/CLAUDE.md ADDED
@@ -0,0 +1,114 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## What this service is
6
+
7
+ `prow-authority` is the central auth/licensing/billing service for the Prow platform ("letsprow"). It is the
8
+ source of truth for users, organizations, memberships/roles, services, licenses/quotas, policies, notifications,
9
+ and billing (Stripe + Holded invoicing). Other Prow services integrate with it via the
10
+ `@beseif-solutions/prow-authority-for-services` client library (see `src/helpers/authority.ts`), which this
11
+ service also uses internally to check quotas/licensing and emit notification events.
12
+
13
+ ## Commands
14
+
15
+ ```bash
16
+ npm run build # tsc compile src/ -> dist/
17
+ npm run build_watch # tsc --watch
18
+ npm run clean # rimraf dist
19
+ npm run run # nodemon dist/index.js, watching dist/**/*.js (runs the already-built output)
20
+ npm run develop # clean + build + build_watch + run, in parallel (main dev loop)
21
+ npm run start # node dist/index.js (no watch, e.g. for prod)
22
+ npm run lint # eslint --ext .ts src --fix
23
+ ```
24
+
25
+ There is no test runner configured in `package.json` — there are no automated tests in this repo currently.
26
+
27
+ A `pre-push` git hook (via husky) runs `npm run build`, so a broken TypeScript build blocks pushes.
28
+
29
+ The server reads its port and all external service credentials from `config/config.yaml` (loaded via
30
+ `@beseif-solutions/utility-functions`'s `YamlParser` in `src/helpers/config.ts`, exposed as `nconf`). There is a
31
+ single `config.yaml` (not environment-specific files), so changes to it affect whichever environment the running
32
+ instance points at — check `SERVICES:RDS:URL` / `SERVICES:AUTHORITY:HOST` before assuming which environment you're
33
+ touching.
34
+
35
+ ## Architecture
36
+
37
+ Layered, per-resource structure under `src/v1/`, mirrored across five parallel folders — a new resource typically
38
+ touches one file in each:
39
+
40
+ ```
41
+ routes/<scope>/<resource>-router.ts Express Router: parses req, calls controller, maps result/errors to HTTP
42
+ controllers/<scope>/<resource>-controller.ts Business logic: validation, orchestration, calls DAOs/other controllers
43
+ daos/<resource>-dao.ts Raw MySQL access (mysql2-style, via @beseif-solutions/utility-functions MySQL)
44
+ schemas/<scope>/<resource>-schemas.ts Joi-like validation schemas (Schema.* from utility-functions) for add/update
45
+ types/<scope>/<resource>-types.ts Domain type (public shape) + DBRecord type (raw DB row shape)
46
+ ```
47
+
48
+ Controllers, routers and DAOs are implemented as singleton classes exported as a default instance
49
+ (`class UserCtrl { ... }` → `export default new UserCtrl()`), not as plain functions/modules — follow this pattern
50
+ for new resources.
51
+
52
+ Every router action follows the same try/catch shape: call the controller, `res.json(response)` on success; on
53
+ error, `if (e instanceof APIError) res.status(e.code).json(e.data)` else 500 with `e.message`. `APIError`
54
+ (`src/helpers/api-error.ts`) is the only structured error type — throw it from controllers/DAOs with a
55
+ `StatusCodes` + `detail` message when a caller-facing failure occurs.
56
+
57
+ Controllers commonly call other controllers directly (not DAOs) when crossing resource boundaries (e.g.
58
+ `user-controller` calls `user-organization-controller` and `identity-token-controller`) — this keeps validation and
59
+ side effects (e.g. cache invalidation, notifications) consistent.
60
+
61
+ ### Request scopes / route mounting (`src/v1/routes/v1-router.ts`)
62
+
63
+ - `/admin/*` — internal/back-office CRUD across nearly every resource (users, organizations, licenses, policies,
64
+ billing, schedules...). Gated by the `admin` middleware (`src/v1/middlewares/static/static-middleware.ts`),
65
+ which checks a static `x-admin-token` header against `SERVICES:AUTH:ADMIN`.
66
+ - `/auth/identity` + `/user` — end-user login/session flows, protected by identity bearer tokens
67
+ (`identity-token-middleware.ts`).
68
+ - `/auth/organization` + `/organization` — organization-scoped session flows (`organization-token-middleware.ts`),
69
+ which resolve and cache an `Environment` (user + organization) in Redis for 60s.
70
+ - `/service`, `/services` (public) — consumed by other Prow services. Authenticated via
71
+ `x-service-uuid`/`x-service-token` headers (`service-token-middleware.ts`), which also resolves and caches a
72
+ `ServiceEnvironment` (service + organization + licenses) in Redis for 60s, and can push license-change
73
+ notifications back to the calling service's configured webhook.
74
+ - `/billing` — Stripe/Holded-backed billing endpoints.
75
+ - `authority-middleware.ts` — used when *this* service's own actions need to check quota (`isPossible`) or license
76
+ status (`organization`) against itself via the `authority` client from `src/helpers/authority.ts`.
77
+
78
+ Auth middlewares consistently split into two steps chained in the router: an `exists` check (pull token out of
79
+ headers, 401 if missing) then a `validate` check (resolve token to a user/environment, attach to `req`). Downstream
80
+ middleware/controllers assume the previous step ran and throw a 500 "Middleware dependency broken" `APIError` if
81
+ the expected `req` property is missing — preserve this ordering when adding routes.
82
+
83
+ ### Data layer
84
+
85
+ - MySQL is the primary store (`src/helpers/mysql.ts`, connection pool of 5). `src/v1/daos/schema.sql` is the
86
+ canonical schema reference (tables for users, organizations, memberships, services, licenses/limits, policies,
87
+ billing cycles, invoices, payment methods, accounting seats). DAOs use `mySql.queryConstructor` for simple
88
+ get/list/delete (built from a `Where<T>` object plus optional `Modifiers` — order/limit/timeout, see
89
+ `src/v1/common-types.ts`) and raw parameterized SQL strings for insert/update.
90
+ - JSON-typed DB columns (`properties`, `preferences`, `metadata`, `internal`, `utm`, `billing`) are
91
+ `JSON.stringify`'d going into DAOs and parsed back out in the controller's `parse()` step, which is also where
92
+ DB rows (`DBRecord`) get reshaped into the public-facing domain type (masking `password`, defaulting
93
+ `preferences.language`, etc).
94
+ - Redis (`src/v1/daos/redis-dao.ts`, generic `RedisDao<T>`) is used purely as a short-lived (60s) cache for
95
+ resolved auth environments, refreshed via `setImmediate` after the response is sent (stale-while-revalidate
96
+ style) rather than blocking the request.
97
+
98
+ ### Scheduling & external integrations
99
+
100
+ - `src/v1/controllers/admin/schedule-controller.ts` registers cron-style jobs on construction (via
101
+ `Scheduler.schedule` from utility-functions) for license-limit checks, license-change fallback sync, billing
102
+ cycle rollover, and invoice generation — these run inside the service process, not as separate jobs.
103
+ - `src/helpers/stripe.ts` / `src/helpers/holded.ts` wrap the Stripe SDK and Holded invoicing API respectively.
104
+ - `src/helpers/notifications.ts` sends transactional email (Gmail templates) and Slack notifications through a
105
+ separate Prow notifications service; `NotificationEventNames` and their payload types are centralized in
106
+ `src/helpers/authority.ts`.
107
+ - `src/helpers/s3.ts` handles avatar/cover image upload for users/organizations/services.
108
+
109
+ ### Deprecated code
110
+
111
+ Files/paths prefixed `@deprecated` (e.g. `controllers/auth/@deprecated oauth-controller.ts`,
112
+ `daos/@deprecated auth-dao.ts`) are kept for reference/rollback but are not part of the active auth flow — the
113
+ active identity/organization token flows are `identity-token-*` and `organization-token-*`. Don't extend the
114
+ deprecated files; treat new auth work as belonging in the token-based controllers/middlewares.
package/Dockerfile ADDED
@@ -0,0 +1,61 @@
1
+ # Imagen de runtime para prow-authority (Node/Express).
2
+ #
3
+ # A diferencia de un front (que hornea sus vars en el bundle en build-time),
4
+ # esta API lee config/config.yaml en RUNTIME (ver src/helpers/config.ts,
5
+ # resuelve la ruta a <workdir>/config/config.yaml). Ese fichero contiene
6
+ # credenciales reales, esta en .gitignore y NUNCA debe copiarse dentro de
7
+ # la imagen. En su lugar se monta como volumen al arrancar el contenedor,
8
+ # apuntando al fichero real gestionado por prow-configuration-files:
9
+ #
10
+ # docker run -v /node/configuration-files/prow-authority.yaml:/app/config/config.yaml:ro ...
11
+ #
12
+ # El unico secret de build es NPM_TOKEN, necesario para instalar los
13
+ # paquetes privados @beseif-solutions/*.
14
+ #
15
+ # Build local:
16
+ # DOCKER_BUILDKIT=1 docker build \
17
+ # --secret id=npm_token,env=NPM_TOKEN \
18
+ # -t prow-authority:local .
19
+ #
20
+ # Run local (con un config.yaml de dev, ej. de prow-configuration-files):
21
+ # docker run -p 5001:5001 -v "$(pwd)/config.yaml:/app/config/config.yaml:ro" prow-authority:local
22
+
23
+ # ---- Stage 1: install deps + build ----------------------------------
24
+ FROM node:20.9.0-bookworm-slim AS builder
25
+ WORKDIR /app
26
+
27
+ COPY package.json package-lock.json ./
28
+ RUN --mount=type=secret,id=npm_token \
29
+ printf '//registry.npmjs.org/:_authToken=%s\n' "$(cat /run/secrets/npm_token)" > .npmrc \
30
+ && npm ci \
31
+ && rm -f .npmrc
32
+
33
+ COPY tsconfig.json ./
34
+ COPY src ./src
35
+ RUN npm run build
36
+
37
+ # ---- Stage 2: production deps only -----------------------------------
38
+ FROM node:20.9.0-bookworm-slim AS deps
39
+ WORKDIR /app
40
+
41
+ COPY package.json package-lock.json ./
42
+ RUN --mount=type=secret,id=npm_token \
43
+ printf '//registry.npmjs.org/:_authToken=%s\n' "$(cat /run/secrets/npm_token)" > .npmrc \
44
+ && npm ci --omit=dev \
45
+ && rm -f .npmrc
46
+
47
+ # ---- Stage 3: runtime --------------------------------------------------
48
+ FROM node:20.9.0-bookworm-slim AS runtime
49
+ WORKDIR /app
50
+ ENV NODE_ENV=production
51
+
52
+ COPY --from=deps /app/node_modules ./node_modules
53
+ COPY --from=builder /app/dist ./dist
54
+ COPY package.json ./
55
+
56
+ # config/config.yaml se monta como volumen (ver cabecera de este fichero).
57
+ # El directorio existe vacio en la imagen para que el mount tenga destino.
58
+ RUN mkdir -p /app/config
59
+
60
+ EXPOSE 5001
61
+ CMD ["node", "dist/index.js"]
package/Dockerfile.dev ADDED
@@ -0,0 +1,24 @@
1
+ # Imagen de desarrollo local (hot-reload). El codigo y config.yaml los
2
+ # proporciona docker-compose en tiempo de ejecucion (bind-mount + volumen con
3
+ # nombre para node_modules) — ver prow-configuration-files/docker-compose.yml,
4
+ # servicio `authority`. Esta imagen solo instala dependencias.
5
+ #
6
+ # El token de npm para @beseif-solutions/* entra como build secret y nunca
7
+ # queda grabado en la imagen.
8
+
9
+ FROM node:20.9.0-bookworm-slim
10
+ WORKDIR /app
11
+
12
+ COPY package.json package-lock.json ./
13
+ RUN --mount=type=secret,id=npm_token \
14
+ printf '//registry.npmjs.org/:_authToken=%s\n' "$(cat /run/secrets/npm_token)" > .npmrc \
15
+ && npm ci \
16
+ && rm -f .npmrc
17
+
18
+ # .npmrc de runtime: resuelve el token desde la variable NPM_TOKEN (inyectada
19
+ # por compose) para poder hacer `npm install <pkg>` dentro del contenedor ya
20
+ # arrancado. Vive en /root para que el bind-mount de /app no lo tape.
21
+ RUN printf '//registry.npmjs.org/:_authToken=${NPM_TOKEN}\n' > /root/.npmrc
22
+
23
+ EXPOSE 5001
24
+ CMD ["npm", "run", "develop"]
package/README.md ADDED
@@ -0,0 +1 @@
1
+ Demo provider for Zapdos-CLI documentation
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "prow-authority",
3
+ "version": "3.6.3",
4
+ "description": "Prow environment user management",
5
+ "scripts": {
6
+ "clean": "rimraf dist",
7
+ "build": "tsc",
8
+ "build_watch": "tsc --watch",
9
+ "run": "nodemon './dist/index.js' --watch './dist/**/*.js'",
10
+ "develop": "npm-run-all clean build --parallel build_watch run",
11
+ "start": "node dist/index.js",
12
+ "lint": "npx eslint -c .eslintrc.js --ext .ts src --fix"
13
+ },
14
+ "keywords": [],
15
+ "author": "Beseif",
16
+ "license": "ISC",
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/beseif-solutions/prow-authority.git"
20
+ },
21
+ "bugs": {
22
+ "url": "https://github.com/beseif-solutions/prow-authority/issues"
23
+ },
24
+ "homepage": "https://github.com/beseif-solutions/prow-authority#readme",
25
+ "dependencies": {
26
+ "@beseif-solutions/prow-authority-for-services": "1.1.0",
27
+ "@beseif-solutions/utility-functions": "1.0.3",
28
+ "@types/node": "^14.18.34",
29
+ "cors": "^2.8.5",
30
+ "country-list": "^2.4.1",
31
+ "country-list-spanish": "^0.3.2",
32
+ "express": "^4.17.1",
33
+ "form-data": "^4.0.2",
34
+ "http-status-codes": "^2.3.0",
35
+ "lodash": "^4.17.21",
36
+ "path": "^0.12.7",
37
+ "stripe": "^18.0.0",
38
+ "ts-node": "^8.10.2",
39
+ "typescript": "4.6.3"
40
+ },
41
+ "devDependencies": {
42
+ "@cpmech/httpcodes": "^3.18.0",
43
+ "@types/cors": "^2.8.17",
44
+ "@types/express": "^4.17.6",
45
+ "@types/lodash": "4.17.5",
46
+ "@typescript-eslint/eslint-plugin": "^5.38.0",
47
+ "@typescript-eslint/parser": "^5.38.0",
48
+ "eslint": "^8.24.0",
49
+ "eslint-plugin-jsdoc": "^39.3.6",
50
+ "eslint-plugin-prefer-arrow": "^1.2.3",
51
+ "husky": "^4.2.5",
52
+ "nodemon": "^2.0.4",
53
+ "npm-run-all": "^4.1.5",
54
+ "rimraf": "^3.0.2"
55
+ },
56
+ "husky": {
57
+ "hooks": {
58
+ "pre-push": "npm run build"
59
+ }
60
+ }
61
+ }
package/src/App.ts ADDED
@@ -0,0 +1,40 @@
1
+ import cors from 'cors';
2
+ import express, { Request, Response } from 'express';
3
+ import httpcodes from 'http-status-codes';
4
+ import v1Router from './v1/routes/v1-router';
5
+
6
+ class App {
7
+ public express: express.Application;
8
+
9
+ constructor() {
10
+ this.express = express();
11
+ this.middleware();
12
+ this.routes();
13
+ }
14
+
15
+ private middleware(): void {
16
+ this.express.use(express.json({ limit: `200mb` }));
17
+ this.express.use(express.urlencoded({ extended: false }));
18
+ this.express.use(cors());
19
+
20
+ this.express.use((req, res, next) => {
21
+ console.log(`-> [${req.method}] ${req.originalUrl}`);
22
+ res.once(`finish`, () => {
23
+ console.log(`<- [${req.method}] ${req.originalUrl}`, res.statusCode);
24
+ });
25
+ next();
26
+ });
27
+ }
28
+
29
+ private readonly notFound = (req: Request, res: Response) => {
30
+ res.status(httpcodes.NOT_FOUND).json({ message: `Not found` });
31
+ };
32
+
33
+ private routes(): void {
34
+ this.express.use(`/v1`, v1Router);
35
+ this.express.use(this.notFound);
36
+ }
37
+ }
38
+
39
+
40
+ export default new App().express;
@@ -0,0 +1,21 @@
1
+
2
+ import { StatusCodes, getReasonPhrase } from 'http-status-codes';
3
+
4
+ export default class APIError extends Error {
5
+
6
+ public readonly code: StatusCodes;
7
+ public readonly data: { code: StatusCodes, message: string, detail: string };
8
+
9
+ constructor(data: { code: StatusCodes, detail: string }) {
10
+ super(`API Error (${data.code}): ${data.detail}`);
11
+ this.code = data.code;
12
+ this.data = {
13
+ code: data.code,
14
+ message: getReasonPhrase(data.code),
15
+ detail: data.detail,
16
+ };
17
+ }
18
+ }
19
+
20
+ export { StatusCodes } from 'http-status-codes';
21
+