@karimov-labs/backstage-plugin-devxp-backend 1.1.0 → 1.2.0

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.
@@ -179,6 +179,52 @@ async function createRouter(options) {
179
179
  });
180
180
  res.json({ message: "Auto-sync triggered" });
181
181
  });
182
+ function analyticsNotConfigured(res) {
183
+ res.status(503).json({
184
+ error: "DevXP analytics not configured. Set devxp.apiEndpoint and devxp.apiToken in app-config.yaml."
185
+ });
186
+ }
187
+ async function proxyAnalytics(path, body, res) {
188
+ if (!apiEndpoint || !apiToken) {
189
+ analyticsNotConfigured(res);
190
+ return;
191
+ }
192
+ try {
193
+ const upstream = await fetch(`${apiEndpoint.replace(/\/$/, "")}/${path}`, {
194
+ method: "POST",
195
+ headers: { "Content-Type": "application/json", Authorization: `Bearer ${apiToken}` },
196
+ body: JSON.stringify(body)
197
+ });
198
+ const data = await upstream.json();
199
+ if (!upstream.ok) {
200
+ logger.warn(`DevXP analytics proxy error [${path}]: ${upstream.status} ${JSON.stringify(data)}`);
201
+ res.status(upstream.status).json(data);
202
+ return;
203
+ }
204
+ res.json(data);
205
+ } catch (e) {
206
+ logger.error(`DevXP analytics proxy failed [${path}]: ${e.message}`);
207
+ res.status(502).json({ error: "Failed to reach DevXP analytics service" });
208
+ }
209
+ }
210
+ router.post("/analytics/dashboard", async (_req, res) => {
211
+ await proxyAnalytics("dashboard", {}, res);
212
+ });
213
+ router.post("/analytics/leaderboard", async (req, res) => {
214
+ await proxyAnalytics("leaderboard", req.body ?? {}, res);
215
+ });
216
+ router.post("/analytics/developer/performance", async (req, res) => {
217
+ await proxyAnalytics("developer/performance", req.body ?? {}, res);
218
+ });
219
+ router.post("/analytics/developer/performance/average", async (req, res) => {
220
+ await proxyAnalytics("developer/performance/average", req.body ?? {}, res);
221
+ });
222
+ router.post("/analytics/repositories", async (req, res) => {
223
+ await proxyAnalytics("repositories", req.body ?? {}, res);
224
+ });
225
+ router.post("/analytics/repository/details", async (req, res) => {
226
+ await proxyAnalytics("repository/details", req.body ?? {}, res);
227
+ });
182
228
  return router;
183
229
  }
184
230
 
@@ -1 +1 @@
1
- {"version":3,"file":"router.cjs.js","sources":["../src/router.ts"],"sourcesContent":["import { createHash } from 'crypto';\nimport { Router } from 'express';\nimport express from 'express';\nimport { Config } from '@backstage/config';\nimport { LoggerService, DatabaseService } from '@backstage/backend-plugin-api';\nimport { MappingStore } from './service/MappingStore';\nimport { GithubSyncStore } from './service/GithubSyncStore';\nimport { GithubSyncService } from './service/GithubSyncService';\n\n/**\n * Hash a username using the same algorithm as dev-xp-analyzer:\n * SHA-256 of (salt + username), truncated to first 16 hex chars.\n */\nfunction hashUsername(salt: string, username: string): string {\n const hash = createHash('sha256')\n .update(`${salt}${username}`)\n .digest('hex');\n return hash.substring(0, 16);\n}\n\ninterface RouterOptions {\n config: Config;\n database: DatabaseService;\n logger: LoggerService;\n}\n\nexport async function createRouter(options: RouterOptions): Promise<express.Router> {\n const { config, database, logger } = options;\n\n const salt = config.getOptionalString('devxp.salt') ?? '';\n const masked = config.getOptionalBoolean('devxp.masked') ?? true;\n const apiEndpoint = config.getOptionalString('devxp.apiEndpoint') ?? '';\n const apiToken = config.getOptionalString('devxp.apiToken') ?? '';\n const projectId = config.getOptionalString('devxp.projectId') ?? '';\n\n const knex = await database.getClient();\n const store = new MappingStore(knex);\n await store.initialize();\n\n const githubSyncStore = new GithubSyncStore(knex);\n await githubSyncStore.initialize();\n\n const githubSyncService = new GithubSyncService(githubSyncStore, store, salt, logger);\n\n logger.info('DevXP backend plugin initialized');\n\n const router = Router();\n router.use(express.json({ limit: '10mb' }));\n\n // Health check\n router.get('/health', async (_req, res) => {\n res.json({ status: 'ok' });\n });\n\n // Return non-sensitive configuration status\n router.get('/config', async (_req, res) => {\n const mappingCount = await store.count();\n res.json({\n masked,\n saltConfigured: salt.length > 0,\n apiEndpointConfigured: apiEndpoint.length > 0,\n apiTokenConfigured: apiToken.length > 0,\n projectIdConfigured: projectId.length > 0,\n mappingCount,\n });\n });\n\n // List all developer mappings\n router.get('/mappings', async (_req, res) => {\n const mappings = await store.getAll();\n res.json({ mappings });\n });\n\n // Upload CSV content with developer names, compute hashes, store mappings\n router.post('/mappings/upload', async (req, res) => {\n const { csvContent } = req.body;\n if (!csvContent || typeof csvContent !== 'string') {\n res.status(400).json({ error: 'csvContent is required as a string' });\n return;\n }\n\n if (!salt) {\n res.status(400).json({ error: 'Salt is not configured. Set devxp.salt in app-config.yaml' });\n return;\n }\n\n const lines = csvContent\n .split(/\\r?\\n/)\n .map(line => line.trim())\n .filter(line => line.length > 0);\n\n // Skip header row if it looks like one\n const startIndex = lines.length > 0 && lines[0].toLowerCase() === 'name' ? 1 : 0;\n\n const mappings: { maskedName: string; realName: string }[] = [];\n for (let i = startIndex; i < lines.length; i++) {\n const realName = lines[i];\n const maskedName = hashUsername(salt, realName);\n mappings.push({ maskedName, realName });\n }\n\n const count = await store.upsertBatch(mappings);\n logger.info(`DevXP: Uploaded ${count} developer mappings`);\n\n res.json({\n message: `Successfully processed ${count} developer name mappings`,\n count,\n });\n });\n\n // Delete a mapping by masked name\n router.post('/mappings/delete', async (req, res) => {\n const { maskedName } = req.body;\n if (!maskedName || typeof maskedName !== 'string') {\n res.status(400).json({ error: 'maskedName is required' });\n return;\n }\n\n const deleted = await store.deleteByMaskedName(maskedName);\n if (deleted) {\n res.json({ message: 'Mapping deleted' });\n } else {\n res.status(404).json({ error: 'Mapping not found' });\n }\n });\n\n // Unmask: given a masked name, return the real name\n router.post('/unmask', async (req, res) => {\n const { maskedName } = req.body;\n if (!maskedName || typeof maskedName !== 'string') {\n res.status(400).json({ error: 'maskedName is required' });\n return;\n }\n\n const mapping = await store.getByMaskedName(maskedName);\n if (mapping) {\n res.json({ maskedName, realName: mapping.real_name });\n } else {\n res.json({ maskedName, realName: null, message: 'No mapping found for this masked name' });\n }\n });\n\n // Hash: given a real name, compute the masked hash\n router.post('/hash', async (req, res) => {\n const { realName } = req.body;\n if (!realName || typeof realName !== 'string') {\n res.status(400).json({ error: 'realName is required' });\n return;\n }\n\n if (!salt) {\n res.status(400).json({ error: 'Salt is not configured. Set devxp.salt in app-config.yaml' });\n return;\n }\n\n const maskedName = hashUsername(salt, realName);\n res.json({ realName, maskedName });\n });\n\n // ─── GitHub Sync Configurations ─────────────────────────────────────────────\n\n // List all GitHub sync configurations (private key excluded)\n router.get('/github-sync', async (_req, res) => {\n const configs = await githubSyncStore.getAll();\n res.json({ configs });\n });\n\n // Register a new GitHub sync configuration\n router.post('/github-sync', async (req, res) => {\n const { orgName, githubHostname, appClientId, appPrivateKey } = req.body;\n if (!orgName || typeof orgName !== 'string') {\n res.status(400).json({ error: 'orgName is required' });\n return;\n }\n if (!appClientId || typeof appClientId !== 'string') {\n res.status(400).json({ error: 'appClientId is required' });\n return;\n }\n if (!appPrivateKey || typeof appPrivateKey !== 'string') {\n res.status(400).json({ error: 'appPrivateKey is required' });\n return;\n }\n\n const hostname = (typeof githubHostname === 'string' && githubHostname.trim())\n ? githubHostname.trim()\n : 'github.com';\n\n const id = await githubSyncStore.create(orgName.trim(), hostname, appClientId.trim(), appPrivateKey.trim());\n logger.info(`DevXP: Registered GitHub sync config for org \"${orgName}\" on \"${hostname}\" (id=${id})`);\n res.json({ id, message: `GitHub sync configuration registered for org \"${orgName}\" on \"${hostname}\"` });\n });\n\n // Toggle active/inactive for a GitHub sync configuration\n router.post('/github-sync/:id/toggle', async (req, res) => {\n const id = parseInt(req.params.id, 10);\n const { active } = req.body;\n if (typeof active !== 'boolean') {\n res.status(400).json({ error: 'active (boolean) is required' });\n return;\n }\n\n const updated = await githubSyncStore.setActive(id, active);\n if (!updated) {\n res.status(404).json({ error: 'GitHub sync configuration not found' });\n return;\n }\n\n res.json({ message: `GitHub sync configuration ${active ? 'activated' : 'deactivated'}` });\n });\n\n // Delete a GitHub sync configuration\n router.delete('/github-sync/:id', async (req, res) => {\n const id = parseInt(req.params.id, 10);\n const deleted = await githubSyncStore.delete(id);\n if (!deleted) {\n res.status(404).json({ error: 'GitHub sync configuration not found' });\n return;\n }\n logger.info(`DevXP: Deleted GitHub sync config id=${id}`);\n res.json({ message: 'GitHub sync configuration deleted' });\n });\n\n // Manually trigger sync for a specific configuration\n router.post('/github-sync/:id/sync', async (req, res) => {\n const id = parseInt(req.params.id, 10);\n try {\n const result = await githubSyncService.syncConfig(id);\n res.json({\n message: `Synced ${result.count} members from \"${result.orgName}\"`,\n count: result.count,\n orgName: result.orgName,\n });\n } catch (e: any) {\n logger.error(`DevXP GitHub manual sync failed for config ${id}: ${e.message}`);\n res.status(500).json({ error: e.message });\n }\n });\n\n // Trigger automatic sync (called by frontend on page load, throttled to 24h)\n router.post('/github-sync/auto', async (_req, res) => {\n // Fire and forget — respond immediately, let sync run in background\n githubSyncService.autoSync().catch((e: any) => {\n logger.warn(`DevXP GitHub auto-sync error: ${e.message}`);\n });\n res.json({ message: 'Auto-sync triggered' });\n });\n\n return router;\n}\n"],"names":["createHash","MappingStore","GithubSyncStore","GithubSyncService","Router","express"],"mappings":"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"}
1
+ {"version":3,"file":"router.cjs.js","sources":["../src/router.ts"],"sourcesContent":["import { createHash } from 'crypto';\nimport { Router } from 'express';\nimport express from 'express';\nimport { Config } from '@backstage/config';\nimport { LoggerService, DatabaseService } from '@backstage/backend-plugin-api';\nimport { MappingStore } from './service/MappingStore';\nimport { GithubSyncStore } from './service/GithubSyncStore';\nimport { GithubSyncService } from './service/GithubSyncService';\n\n/**\n * Hash a username using the same algorithm as dev-xp-analyzer:\n * SHA-256 of (salt + username), truncated to first 16 hex chars.\n */\nfunction hashUsername(salt: string, username: string): string {\n const hash = createHash('sha256')\n .update(`${salt}${username}`)\n .digest('hex');\n return hash.substring(0, 16);\n}\n\ninterface RouterOptions {\n config: Config;\n database: DatabaseService;\n logger: LoggerService;\n}\n\nexport async function createRouter(options: RouterOptions): Promise<express.Router> {\n const { config, database, logger } = options;\n\n const salt = config.getOptionalString('devxp.salt') ?? '';\n const masked = config.getOptionalBoolean('devxp.masked') ?? true;\n const apiEndpoint = config.getOptionalString('devxp.apiEndpoint') ?? '';\n const apiToken = config.getOptionalString('devxp.apiToken') ?? '';\n const projectId = config.getOptionalString('devxp.projectId') ?? '';\n\n const knex = await database.getClient();\n const store = new MappingStore(knex);\n await store.initialize();\n\n const githubSyncStore = new GithubSyncStore(knex);\n await githubSyncStore.initialize();\n\n const githubSyncService = new GithubSyncService(githubSyncStore, store, salt, logger);\n\n logger.info('DevXP backend plugin initialized');\n\n const router = Router();\n router.use(express.json({ limit: '10mb' }));\n\n // Health check\n router.get('/health', async (_req, res) => {\n res.json({ status: 'ok' });\n });\n\n // Return non-sensitive configuration status\n router.get('/config', async (_req, res) => {\n const mappingCount = await store.count();\n res.json({\n masked,\n saltConfigured: salt.length > 0,\n apiEndpointConfigured: apiEndpoint.length > 0,\n apiTokenConfigured: apiToken.length > 0,\n projectIdConfigured: projectId.length > 0,\n mappingCount,\n });\n });\n\n // List all developer mappings\n router.get('/mappings', async (_req, res) => {\n const mappings = await store.getAll();\n res.json({ mappings });\n });\n\n // Upload CSV content with developer names, compute hashes, store mappings\n router.post('/mappings/upload', async (req, res) => {\n const { csvContent } = req.body;\n if (!csvContent || typeof csvContent !== 'string') {\n res.status(400).json({ error: 'csvContent is required as a string' });\n return;\n }\n\n if (!salt) {\n res.status(400).json({ error: 'Salt is not configured. Set devxp.salt in app-config.yaml' });\n return;\n }\n\n const lines = csvContent\n .split(/\\r?\\n/)\n .map(line => line.trim())\n .filter(line => line.length > 0);\n\n // Skip header row if it looks like one\n const startIndex = lines.length > 0 && lines[0].toLowerCase() === 'name' ? 1 : 0;\n\n const mappings: { maskedName: string; realName: string }[] = [];\n for (let i = startIndex; i < lines.length; i++) {\n const realName = lines[i];\n const maskedName = hashUsername(salt, realName);\n mappings.push({ maskedName, realName });\n }\n\n const count = await store.upsertBatch(mappings);\n logger.info(`DevXP: Uploaded ${count} developer mappings`);\n\n res.json({\n message: `Successfully processed ${count} developer name mappings`,\n count,\n });\n });\n\n // Delete a mapping by masked name\n router.post('/mappings/delete', async (req, res) => {\n const { maskedName } = req.body;\n if (!maskedName || typeof maskedName !== 'string') {\n res.status(400).json({ error: 'maskedName is required' });\n return;\n }\n\n const deleted = await store.deleteByMaskedName(maskedName);\n if (deleted) {\n res.json({ message: 'Mapping deleted' });\n } else {\n res.status(404).json({ error: 'Mapping not found' });\n }\n });\n\n // Unmask: given a masked name, return the real name\n router.post('/unmask', async (req, res) => {\n const { maskedName } = req.body;\n if (!maskedName || typeof maskedName !== 'string') {\n res.status(400).json({ error: 'maskedName is required' });\n return;\n }\n\n const mapping = await store.getByMaskedName(maskedName);\n if (mapping) {\n res.json({ maskedName, realName: mapping.real_name });\n } else {\n res.json({ maskedName, realName: null, message: 'No mapping found for this masked name' });\n }\n });\n\n // Hash: given a real name, compute the masked hash\n router.post('/hash', async (req, res) => {\n const { realName } = req.body;\n if (!realName || typeof realName !== 'string') {\n res.status(400).json({ error: 'realName is required' });\n return;\n }\n\n if (!salt) {\n res.status(400).json({ error: 'Salt is not configured. Set devxp.salt in app-config.yaml' });\n return;\n }\n\n const maskedName = hashUsername(salt, realName);\n res.json({ realName, maskedName });\n });\n\n // ─── GitHub Sync Configurations ─────────────────────────────────────────────\n\n // List all GitHub sync configurations (private key excluded)\n router.get('/github-sync', async (_req, res) => {\n const configs = await githubSyncStore.getAll();\n res.json({ configs });\n });\n\n // Register a new GitHub sync configuration\n router.post('/github-sync', async (req, res) => {\n const { orgName, githubHostname, appClientId, appPrivateKey } = req.body;\n if (!orgName || typeof orgName !== 'string') {\n res.status(400).json({ error: 'orgName is required' });\n return;\n }\n if (!appClientId || typeof appClientId !== 'string') {\n res.status(400).json({ error: 'appClientId is required' });\n return;\n }\n if (!appPrivateKey || typeof appPrivateKey !== 'string') {\n res.status(400).json({ error: 'appPrivateKey is required' });\n return;\n }\n\n const hostname = (typeof githubHostname === 'string' && githubHostname.trim())\n ? githubHostname.trim()\n : 'github.com';\n\n const id = await githubSyncStore.create(orgName.trim(), hostname, appClientId.trim(), appPrivateKey.trim());\n logger.info(`DevXP: Registered GitHub sync config for org \"${orgName}\" on \"${hostname}\" (id=${id})`);\n res.json({ id, message: `GitHub sync configuration registered for org \"${orgName}\" on \"${hostname}\"` });\n });\n\n // Toggle active/inactive for a GitHub sync configuration\n router.post('/github-sync/:id/toggle', async (req, res) => {\n const id = parseInt(req.params.id, 10);\n const { active } = req.body;\n if (typeof active !== 'boolean') {\n res.status(400).json({ error: 'active (boolean) is required' });\n return;\n }\n\n const updated = await githubSyncStore.setActive(id, active);\n if (!updated) {\n res.status(404).json({ error: 'GitHub sync configuration not found' });\n return;\n }\n\n res.json({ message: `GitHub sync configuration ${active ? 'activated' : 'deactivated'}` });\n });\n\n // Delete a GitHub sync configuration\n router.delete('/github-sync/:id', async (req, res) => {\n const id = parseInt(req.params.id, 10);\n const deleted = await githubSyncStore.delete(id);\n if (!deleted) {\n res.status(404).json({ error: 'GitHub sync configuration not found' });\n return;\n }\n logger.info(`DevXP: Deleted GitHub sync config id=${id}`);\n res.json({ message: 'GitHub sync configuration deleted' });\n });\n\n // Manually trigger sync for a specific configuration\n router.post('/github-sync/:id/sync', async (req, res) => {\n const id = parseInt(req.params.id, 10);\n try {\n const result = await githubSyncService.syncConfig(id);\n res.json({\n message: `Synced ${result.count} members from \"${result.orgName}\"`,\n count: result.count,\n orgName: result.orgName,\n });\n } catch (e: any) {\n logger.error(`DevXP GitHub manual sync failed for config ${id}: ${e.message}`);\n res.status(500).json({ error: e.message });\n }\n });\n\n // Trigger automatic sync (called by frontend on page load, throttled to 24h)\n router.post('/github-sync/auto', async (_req, res) => {\n // Fire and forget — respond immediately, let sync run in background\n githubSyncService.autoSync().catch((e: any) => {\n logger.warn(`DevXP GitHub auto-sync error: ${e.message}`);\n });\n res.json({ message: 'Auto-sync triggered' });\n });\n\n // ─── Analytics (proxy to plugin-integration Appwrite function) ──────────────\n\n function analyticsNotConfigured(res: any) {\n res.status(503).json({\n error: 'DevXP analytics not configured. Set devxp.apiEndpoint and devxp.apiToken in app-config.yaml.',\n });\n }\n\n async function proxyAnalytics(path: string, body: any, res: any) {\n if (!apiEndpoint || !apiToken) {\n analyticsNotConfigured(res);\n return;\n }\n try {\n const upstream = await fetch(`${apiEndpoint.replace(/\\/$/, '')}/${path}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${apiToken}` },\n body: JSON.stringify(body),\n });\n const data = await upstream.json();\n if (!upstream.ok) {\n logger.warn(`DevXP analytics proxy error [${path}]: ${upstream.status} ${JSON.stringify(data)}`);\n res.status(upstream.status).json(data);\n return;\n }\n res.json(data);\n } catch (e: any) {\n logger.error(`DevXP analytics proxy failed [${path}]: ${e.message}`);\n res.status(502).json({ error: 'Failed to reach DevXP analytics service' });\n }\n }\n\n router.post('/analytics/dashboard', async (_req, res) => {\n await proxyAnalytics('dashboard', {}, res);\n });\n\n router.post('/analytics/leaderboard', async (req, res) => {\n await proxyAnalytics('leaderboard', req.body ?? {}, res);\n });\n\n router.post('/analytics/developer/performance', async (req, res) => {\n await proxyAnalytics('developer/performance', req.body ?? {}, res);\n });\n\n router.post('/analytics/developer/performance/average', async (req, res) => {\n await proxyAnalytics('developer/performance/average', req.body ?? {}, res);\n });\n\n router.post('/analytics/repositories', async (req, res) => {\n await proxyAnalytics('repositories', req.body ?? {}, res);\n });\n\n router.post('/analytics/repository/details', async (req, res) => {\n await proxyAnalytics('repository/details', req.body ?? {}, res);\n });\n\n return router;\n}\n"],"names":["createHash","MappingStore","GithubSyncStore","GithubSyncService","Router","express"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@karimov-labs/backstage-plugin-devxp-backend",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "Backstage backend plugin for developer intelligence — SHA-256 identity hashing, developer name mappings, and CSV ingestion.",
5
5
  "main": "dist/index.cjs.js",
6
6
  "types": "dist/index.d.ts",