@push.rocks/smartregistry 1.4.1 → 1.6.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.
- package/dist_ts/00_commitinfo_data.js +3 -3
- package/dist_ts/classes.smartregistry.d.ts +2 -2
- package/dist_ts/classes.smartregistry.js +37 -2
- package/dist_ts/core/classes.authmanager.d.ts +55 -1
- package/dist_ts/core/classes.authmanager.js +138 -3
- package/dist_ts/core/classes.registrystorage.d.ts +145 -0
- package/dist_ts/core/classes.registrystorage.js +392 -1
- package/dist_ts/core/interfaces.core.d.ts +13 -1
- package/dist_ts/index.d.ts +3 -1
- package/dist_ts/index.js +6 -2
- package/dist_ts/pypi/classes.pypiregistry.d.ts +70 -0
- package/dist_ts/pypi/classes.pypiregistry.js +482 -0
- package/dist_ts/pypi/helpers.pypi.d.ts +84 -0
- package/dist_ts/pypi/helpers.pypi.js +263 -0
- package/dist_ts/pypi/index.d.ts +7 -0
- package/dist_ts/pypi/index.js +8 -0
- package/dist_ts/pypi/interfaces.pypi.d.ts +301 -0
- package/dist_ts/pypi/interfaces.pypi.js +6 -0
- package/dist_ts/rubygems/classes.rubygemsregistry.d.ts +86 -0
- package/dist_ts/rubygems/classes.rubygemsregistry.js +475 -0
- package/dist_ts/rubygems/helpers.rubygems.d.ts +143 -0
- package/dist_ts/rubygems/helpers.rubygems.js +312 -0
- package/dist_ts/rubygems/index.d.ts +7 -0
- package/dist_ts/rubygems/index.js +8 -0
- package/dist_ts/rubygems/interfaces.rubygems.d.ts +236 -0
- package/dist_ts/rubygems/interfaces.rubygems.js +6 -0
- package/package.json +2 -2
- package/readme.hints.md +438 -2
- package/readme.md +288 -13
- package/ts/00_commitinfo_data.ts +2 -2
- package/ts/classes.smartregistry.ts +41 -2
- package/ts/core/classes.authmanager.ts +161 -2
- package/ts/core/classes.registrystorage.ts +463 -0
- package/ts/core/interfaces.core.ts +13 -1
- package/ts/index.ts +7 -1
- package/ts/pypi/classes.pypiregistry.ts +580 -0
- package/ts/pypi/helpers.pypi.ts +299 -0
- package/ts/pypi/index.ts +8 -0
- package/ts/pypi/interfaces.pypi.ts +316 -0
- package/ts/rubygems/classes.rubygemsregistry.ts +598 -0
- package/ts/rubygems/helpers.rubygems.ts +398 -0
- package/ts/rubygems/index.ts +8 -0
- package/ts/rubygems/interfaces.rubygems.ts +251 -0
|
@@ -0,0 +1,475 @@
|
|
|
1
|
+
import { Smartlog } from '@push.rocks/smartlog';
|
|
2
|
+
import { BaseRegistry } from '../core/classes.baseregistry.js';
|
|
3
|
+
import { RegistryStorage } from '../core/classes.registrystorage.js';
|
|
4
|
+
import { AuthManager } from '../core/classes.authmanager.js';
|
|
5
|
+
import * as helpers from './helpers.rubygems.js';
|
|
6
|
+
/**
|
|
7
|
+
* RubyGems registry implementation
|
|
8
|
+
* Implements Compact Index API and RubyGems protocol
|
|
9
|
+
*/
|
|
10
|
+
export class RubyGemsRegistry extends BaseRegistry {
|
|
11
|
+
storage;
|
|
12
|
+
authManager;
|
|
13
|
+
basePath = '/rubygems';
|
|
14
|
+
registryUrl;
|
|
15
|
+
logger;
|
|
16
|
+
constructor(storage, authManager, basePath = '/rubygems', registryUrl = 'http://localhost:5000/rubygems') {
|
|
17
|
+
super();
|
|
18
|
+
this.storage = storage;
|
|
19
|
+
this.authManager = authManager;
|
|
20
|
+
this.basePath = basePath;
|
|
21
|
+
this.registryUrl = registryUrl;
|
|
22
|
+
// Initialize logger
|
|
23
|
+
this.logger = new Smartlog({
|
|
24
|
+
logContext: {
|
|
25
|
+
company: 'push.rocks',
|
|
26
|
+
companyunit: 'smartregistry',
|
|
27
|
+
containerName: 'rubygems-registry',
|
|
28
|
+
environment: process.env.NODE_ENV || 'development',
|
|
29
|
+
runtime: 'node',
|
|
30
|
+
zone: 'rubygems'
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
this.logger.enableConsole();
|
|
34
|
+
}
|
|
35
|
+
async init() {
|
|
36
|
+
// Initialize Compact Index files if not exist
|
|
37
|
+
const existingVersions = await this.storage.getRubyGemsVersions();
|
|
38
|
+
if (!existingVersions) {
|
|
39
|
+
const versions = helpers.generateCompactIndexVersions([]);
|
|
40
|
+
await this.storage.putRubyGemsVersions(versions);
|
|
41
|
+
this.logger.log('info', 'Initialized RubyGems Compact Index');
|
|
42
|
+
}
|
|
43
|
+
const existingNames = await this.storage.getRubyGemsNames();
|
|
44
|
+
if (!existingNames) {
|
|
45
|
+
const names = helpers.generateNamesFile([]);
|
|
46
|
+
await this.storage.putRubyGemsNames(names);
|
|
47
|
+
this.logger.log('info', 'Initialized RubyGems names file');
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
getBasePath() {
|
|
51
|
+
return this.basePath;
|
|
52
|
+
}
|
|
53
|
+
async handleRequest(context) {
|
|
54
|
+
let path = context.path.replace(this.basePath, '');
|
|
55
|
+
// Extract token (Authorization header)
|
|
56
|
+
const token = await this.extractToken(context);
|
|
57
|
+
this.logger.log('debug', `handleRequest: ${context.method} ${path}`, {
|
|
58
|
+
method: context.method,
|
|
59
|
+
path,
|
|
60
|
+
hasAuth: !!token
|
|
61
|
+
});
|
|
62
|
+
// Compact Index endpoints
|
|
63
|
+
if (path === '/versions' && context.method === 'GET') {
|
|
64
|
+
return this.handleVersionsFile();
|
|
65
|
+
}
|
|
66
|
+
if (path === '/names' && context.method === 'GET') {
|
|
67
|
+
return this.handleNamesFile();
|
|
68
|
+
}
|
|
69
|
+
// Info file: GET /info/{gem}
|
|
70
|
+
const infoMatch = path.match(/^\/info\/([^\/]+)$/);
|
|
71
|
+
if (infoMatch && context.method === 'GET') {
|
|
72
|
+
return this.handleInfoFile(infoMatch[1]);
|
|
73
|
+
}
|
|
74
|
+
// Gem download: GET /gems/{gem}-{version}[-{platform}].gem
|
|
75
|
+
const downloadMatch = path.match(/^\/gems\/(.+\.gem)$/);
|
|
76
|
+
if (downloadMatch && context.method === 'GET') {
|
|
77
|
+
return this.handleDownload(downloadMatch[1]);
|
|
78
|
+
}
|
|
79
|
+
// API v1 endpoints
|
|
80
|
+
if (path.startsWith('/api/v1/')) {
|
|
81
|
+
return this.handleApiRequest(path.substring(8), context, token);
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
status: 404,
|
|
85
|
+
headers: { 'Content-Type': 'application/json' },
|
|
86
|
+
body: Buffer.from(JSON.stringify({ message: 'Not Found' })),
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Check if token has permission for resource
|
|
91
|
+
*/
|
|
92
|
+
async checkPermission(token, resource, action) {
|
|
93
|
+
if (!token)
|
|
94
|
+
return false;
|
|
95
|
+
return this.authManager.authorize(token, `rubygems:gem:${resource}`, action);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Extract authentication token from request
|
|
99
|
+
*/
|
|
100
|
+
async extractToken(context) {
|
|
101
|
+
const authHeader = context.headers['authorization'] || context.headers['Authorization'];
|
|
102
|
+
if (!authHeader)
|
|
103
|
+
return null;
|
|
104
|
+
// RubyGems typically uses plain API key in Authorization header
|
|
105
|
+
return this.authManager.validateToken(authHeader, 'rubygems');
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Handle /versions endpoint (Compact Index)
|
|
109
|
+
*/
|
|
110
|
+
async handleVersionsFile() {
|
|
111
|
+
const content = await this.storage.getRubyGemsVersions();
|
|
112
|
+
if (!content) {
|
|
113
|
+
return this.errorResponse(500, 'Versions file not initialized');
|
|
114
|
+
}
|
|
115
|
+
return {
|
|
116
|
+
status: 200,
|
|
117
|
+
headers: {
|
|
118
|
+
'Content-Type': 'text/plain; charset=utf-8',
|
|
119
|
+
'Cache-Control': 'public, max-age=60',
|
|
120
|
+
'ETag': `"${await helpers.calculateMD5(content)}"`
|
|
121
|
+
},
|
|
122
|
+
body: Buffer.from(content),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Handle /names endpoint (Compact Index)
|
|
127
|
+
*/
|
|
128
|
+
async handleNamesFile() {
|
|
129
|
+
const content = await this.storage.getRubyGemsNames();
|
|
130
|
+
if (!content) {
|
|
131
|
+
return this.errorResponse(500, 'Names file not initialized');
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
status: 200,
|
|
135
|
+
headers: {
|
|
136
|
+
'Content-Type': 'text/plain; charset=utf-8',
|
|
137
|
+
'Cache-Control': 'public, max-age=300'
|
|
138
|
+
},
|
|
139
|
+
body: Buffer.from(content),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Handle /info/{gem} endpoint (Compact Index)
|
|
144
|
+
*/
|
|
145
|
+
async handleInfoFile(gemName) {
|
|
146
|
+
const content = await this.storage.getRubyGemsInfo(gemName);
|
|
147
|
+
if (!content) {
|
|
148
|
+
return {
|
|
149
|
+
status: 404,
|
|
150
|
+
headers: { 'Content-Type': 'text/plain' },
|
|
151
|
+
body: Buffer.from('Not Found'),
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
return {
|
|
155
|
+
status: 200,
|
|
156
|
+
headers: {
|
|
157
|
+
'Content-Type': 'text/plain; charset=utf-8',
|
|
158
|
+
'Cache-Control': 'public, max-age=300',
|
|
159
|
+
'ETag': `"${await helpers.calculateMD5(content)}"`
|
|
160
|
+
},
|
|
161
|
+
body: Buffer.from(content),
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Handle gem file download
|
|
166
|
+
*/
|
|
167
|
+
async handleDownload(filename) {
|
|
168
|
+
const parsed = helpers.parseGemFilename(filename);
|
|
169
|
+
if (!parsed) {
|
|
170
|
+
return this.errorResponse(400, 'Invalid gem filename');
|
|
171
|
+
}
|
|
172
|
+
const gemData = await this.storage.getRubyGemsGem(parsed.name, parsed.version, parsed.platform);
|
|
173
|
+
if (!gemData) {
|
|
174
|
+
return this.errorResponse(404, 'Gem not found');
|
|
175
|
+
}
|
|
176
|
+
return {
|
|
177
|
+
status: 200,
|
|
178
|
+
headers: {
|
|
179
|
+
'Content-Type': 'application/octet-stream',
|
|
180
|
+
'Content-Disposition': `attachment; filename="${filename}"`,
|
|
181
|
+
'Content-Length': gemData.length.toString()
|
|
182
|
+
},
|
|
183
|
+
body: gemData,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Handle API v1 requests
|
|
188
|
+
*/
|
|
189
|
+
async handleApiRequest(path, context, token) {
|
|
190
|
+
// Upload gem: POST /gems
|
|
191
|
+
if (path === '/gems' && context.method === 'POST') {
|
|
192
|
+
return this.handleUpload(context, token);
|
|
193
|
+
}
|
|
194
|
+
// Yank gem: DELETE /gems/yank
|
|
195
|
+
if (path === '/gems/yank' && context.method === 'DELETE') {
|
|
196
|
+
return this.handleYank(context, token);
|
|
197
|
+
}
|
|
198
|
+
// Unyank gem: PUT /gems/unyank
|
|
199
|
+
if (path === '/gems/unyank' && context.method === 'PUT') {
|
|
200
|
+
return this.handleUnyank(context, token);
|
|
201
|
+
}
|
|
202
|
+
// Version list: GET /versions/{gem}.json
|
|
203
|
+
const versionsMatch = path.match(/^\/versions\/([^\/]+)\.json$/);
|
|
204
|
+
if (versionsMatch && context.method === 'GET') {
|
|
205
|
+
return this.handleVersionsJson(versionsMatch[1]);
|
|
206
|
+
}
|
|
207
|
+
// Dependencies: GET /dependencies?gems={list}
|
|
208
|
+
if (path.startsWith('/dependencies') && context.method === 'GET') {
|
|
209
|
+
const gemsParam = context.query?.gems || '';
|
|
210
|
+
return this.handleDependencies(gemsParam);
|
|
211
|
+
}
|
|
212
|
+
return this.errorResponse(404, 'API endpoint not found');
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Handle gem upload
|
|
216
|
+
* POST /api/v1/gems
|
|
217
|
+
*/
|
|
218
|
+
async handleUpload(context, token) {
|
|
219
|
+
if (!token) {
|
|
220
|
+
return this.errorResponse(401, 'Authentication required');
|
|
221
|
+
}
|
|
222
|
+
try {
|
|
223
|
+
// Extract gem data from request body
|
|
224
|
+
const gemData = context.body;
|
|
225
|
+
if (!gemData || gemData.length === 0) {
|
|
226
|
+
return this.errorResponse(400, 'No gem file provided');
|
|
227
|
+
}
|
|
228
|
+
// For now, we expect metadata in query params or headers
|
|
229
|
+
// Full implementation would parse .gem file (tar + gzip + Marshal)
|
|
230
|
+
const gemName = context.query?.name || context.headers['x-gem-name'];
|
|
231
|
+
const version = context.query?.version || context.headers['x-gem-version'];
|
|
232
|
+
const platform = context.query?.platform || context.headers['x-gem-platform'];
|
|
233
|
+
if (!gemName || !version) {
|
|
234
|
+
return this.errorResponse(400, 'Gem name and version required');
|
|
235
|
+
}
|
|
236
|
+
// Validate gem name
|
|
237
|
+
if (!helpers.isValidGemName(gemName)) {
|
|
238
|
+
return this.errorResponse(400, 'Invalid gem name');
|
|
239
|
+
}
|
|
240
|
+
// Check permission
|
|
241
|
+
if (!(await this.checkPermission(token, gemName, 'write'))) {
|
|
242
|
+
return this.errorResponse(403, 'Insufficient permissions');
|
|
243
|
+
}
|
|
244
|
+
// Calculate checksum
|
|
245
|
+
const checksum = await helpers.calculateSHA256(gemData);
|
|
246
|
+
// Store gem file
|
|
247
|
+
await this.storage.putRubyGemsGem(gemName, version, gemData, platform);
|
|
248
|
+
// Update metadata
|
|
249
|
+
let metadata = await this.storage.getRubyGemsMetadata(gemName) || {
|
|
250
|
+
name: gemName,
|
|
251
|
+
versions: {},
|
|
252
|
+
};
|
|
253
|
+
const versionKey = platform ? `${version}-${platform}` : version;
|
|
254
|
+
metadata.versions[versionKey] = {
|
|
255
|
+
version,
|
|
256
|
+
platform,
|
|
257
|
+
checksum,
|
|
258
|
+
size: gemData.length,
|
|
259
|
+
'upload-time': new Date().toISOString(),
|
|
260
|
+
'uploaded-by': token.userId,
|
|
261
|
+
dependencies: [], // Would extract from gem spec
|
|
262
|
+
requirements: [],
|
|
263
|
+
};
|
|
264
|
+
metadata['last-modified'] = new Date().toISOString();
|
|
265
|
+
await this.storage.putRubyGemsMetadata(gemName, metadata);
|
|
266
|
+
// Update Compact Index info file
|
|
267
|
+
await this.updateCompactIndexForGem(gemName, metadata);
|
|
268
|
+
// Update versions file
|
|
269
|
+
await this.updateVersionsFile(gemName, version, platform || 'ruby', false);
|
|
270
|
+
// Update names file
|
|
271
|
+
await this.updateNamesFile(gemName);
|
|
272
|
+
this.logger.log('info', `Gem uploaded: ${gemName} ${version}`, {
|
|
273
|
+
platform,
|
|
274
|
+
size: gemData.length
|
|
275
|
+
});
|
|
276
|
+
return {
|
|
277
|
+
status: 200,
|
|
278
|
+
headers: { 'Content-Type': 'application/json' },
|
|
279
|
+
body: Buffer.from(JSON.stringify({
|
|
280
|
+
message: 'Gem uploaded successfully',
|
|
281
|
+
name: gemName,
|
|
282
|
+
version,
|
|
283
|
+
})),
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
catch (error) {
|
|
287
|
+
this.logger.log('error', 'Upload failed', { error: error.message });
|
|
288
|
+
return this.errorResponse(500, 'Upload failed: ' + error.message);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Handle gem yanking
|
|
293
|
+
* DELETE /api/v1/gems/yank
|
|
294
|
+
*/
|
|
295
|
+
async handleYank(context, token) {
|
|
296
|
+
if (!token) {
|
|
297
|
+
return this.errorResponse(401, 'Authentication required');
|
|
298
|
+
}
|
|
299
|
+
const gemName = context.query?.gem_name;
|
|
300
|
+
const version = context.query?.version;
|
|
301
|
+
const platform = context.query?.platform;
|
|
302
|
+
if (!gemName || !version) {
|
|
303
|
+
return this.errorResponse(400, 'Gem name and version required');
|
|
304
|
+
}
|
|
305
|
+
if (!(await this.checkPermission(token, gemName, 'yank'))) {
|
|
306
|
+
return this.errorResponse(403, 'Insufficient permissions');
|
|
307
|
+
}
|
|
308
|
+
// Update metadata to mark as yanked
|
|
309
|
+
const metadata = await this.storage.getRubyGemsMetadata(gemName);
|
|
310
|
+
if (!metadata) {
|
|
311
|
+
return this.errorResponse(404, 'Gem not found');
|
|
312
|
+
}
|
|
313
|
+
const versionKey = platform ? `${version}-${platform}` : version;
|
|
314
|
+
if (!metadata.versions[versionKey]) {
|
|
315
|
+
return this.errorResponse(404, 'Version not found');
|
|
316
|
+
}
|
|
317
|
+
metadata.versions[versionKey].yanked = true;
|
|
318
|
+
await this.storage.putRubyGemsMetadata(gemName, metadata);
|
|
319
|
+
// Update Compact Index
|
|
320
|
+
await this.updateCompactIndexForGem(gemName, metadata);
|
|
321
|
+
await this.updateVersionsFile(gemName, version, platform || 'ruby', true);
|
|
322
|
+
this.logger.log('info', `Gem yanked: ${gemName} ${version}`);
|
|
323
|
+
return {
|
|
324
|
+
status: 200,
|
|
325
|
+
headers: { 'Content-Type': 'application/json' },
|
|
326
|
+
body: Buffer.from(JSON.stringify({
|
|
327
|
+
success: true,
|
|
328
|
+
message: 'Gem yanked successfully'
|
|
329
|
+
})),
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Handle gem unyanking
|
|
334
|
+
* PUT /api/v1/gems/unyank
|
|
335
|
+
*/
|
|
336
|
+
async handleUnyank(context, token) {
|
|
337
|
+
if (!token) {
|
|
338
|
+
return this.errorResponse(401, 'Authentication required');
|
|
339
|
+
}
|
|
340
|
+
const gemName = context.query?.gem_name;
|
|
341
|
+
const version = context.query?.version;
|
|
342
|
+
const platform = context.query?.platform;
|
|
343
|
+
if (!gemName || !version) {
|
|
344
|
+
return this.errorResponse(400, 'Gem name and version required');
|
|
345
|
+
}
|
|
346
|
+
if (!(await this.checkPermission(token, gemName, 'write'))) {
|
|
347
|
+
return this.errorResponse(403, 'Insufficient permissions');
|
|
348
|
+
}
|
|
349
|
+
const metadata = await this.storage.getRubyGemsMetadata(gemName);
|
|
350
|
+
if (!metadata) {
|
|
351
|
+
return this.errorResponse(404, 'Gem not found');
|
|
352
|
+
}
|
|
353
|
+
const versionKey = platform ? `${version}-${platform}` : version;
|
|
354
|
+
if (!metadata.versions[versionKey]) {
|
|
355
|
+
return this.errorResponse(404, 'Version not found');
|
|
356
|
+
}
|
|
357
|
+
metadata.versions[versionKey].yanked = false;
|
|
358
|
+
await this.storage.putRubyGemsMetadata(gemName, metadata);
|
|
359
|
+
// Update Compact Index
|
|
360
|
+
await this.updateCompactIndexForGem(gemName, metadata);
|
|
361
|
+
await this.updateVersionsFile(gemName, version, platform || 'ruby', false);
|
|
362
|
+
this.logger.log('info', `Gem unyanked: ${gemName} ${version}`);
|
|
363
|
+
return {
|
|
364
|
+
status: 200,
|
|
365
|
+
headers: { 'Content-Type': 'application/json' },
|
|
366
|
+
body: Buffer.from(JSON.stringify({
|
|
367
|
+
success: true,
|
|
368
|
+
message: 'Gem unyanked successfully'
|
|
369
|
+
})),
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Handle versions JSON API
|
|
374
|
+
*/
|
|
375
|
+
async handleVersionsJson(gemName) {
|
|
376
|
+
const metadata = await this.storage.getRubyGemsMetadata(gemName);
|
|
377
|
+
if (!metadata) {
|
|
378
|
+
return this.errorResponse(404, 'Gem not found');
|
|
379
|
+
}
|
|
380
|
+
const versions = Object.values(metadata.versions).map((v) => ({
|
|
381
|
+
version: v.version,
|
|
382
|
+
platform: v.platform,
|
|
383
|
+
uploadTime: v['upload-time'],
|
|
384
|
+
}));
|
|
385
|
+
const response = helpers.generateVersionsJson(gemName, versions);
|
|
386
|
+
return {
|
|
387
|
+
status: 200,
|
|
388
|
+
headers: {
|
|
389
|
+
'Content-Type': 'application/json',
|
|
390
|
+
'Cache-Control': 'public, max-age=300'
|
|
391
|
+
},
|
|
392
|
+
body: Buffer.from(JSON.stringify(response)),
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* Handle dependencies query
|
|
397
|
+
*/
|
|
398
|
+
async handleDependencies(gemsParam) {
|
|
399
|
+
const gemNames = gemsParam.split(',').filter(n => n.trim());
|
|
400
|
+
const result = new Map();
|
|
401
|
+
for (const gemName of gemNames) {
|
|
402
|
+
const metadata = await this.storage.getRubyGemsMetadata(gemName);
|
|
403
|
+
if (metadata) {
|
|
404
|
+
const versions = Object.values(metadata.versions).map((v) => ({
|
|
405
|
+
version: v.version,
|
|
406
|
+
platform: v.platform,
|
|
407
|
+
dependencies: v.dependencies || [],
|
|
408
|
+
}));
|
|
409
|
+
result.set(gemName, versions);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
const response = helpers.generateDependenciesJson(result);
|
|
413
|
+
return {
|
|
414
|
+
status: 200,
|
|
415
|
+
headers: { 'Content-Type': 'application/json' },
|
|
416
|
+
body: Buffer.from(JSON.stringify(response)),
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Update Compact Index info file for a gem
|
|
421
|
+
*/
|
|
422
|
+
async updateCompactIndexForGem(gemName, metadata) {
|
|
423
|
+
const entries = Object.values(metadata.versions)
|
|
424
|
+
.filter(v => !v.yanked) // Exclude yanked from info file
|
|
425
|
+
.map(v => ({
|
|
426
|
+
version: v.version,
|
|
427
|
+
platform: v.platform,
|
|
428
|
+
dependencies: v.dependencies || [],
|
|
429
|
+
requirements: v.requirements || [],
|
|
430
|
+
checksum: v.checksum,
|
|
431
|
+
}));
|
|
432
|
+
const content = helpers.generateCompactIndexInfo(entries);
|
|
433
|
+
await this.storage.putRubyGemsInfo(gemName, content);
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Update versions file with new/updated gem
|
|
437
|
+
*/
|
|
438
|
+
async updateVersionsFile(gemName, version, platform, yanked) {
|
|
439
|
+
const existingVersions = await this.storage.getRubyGemsVersions();
|
|
440
|
+
if (!existingVersions)
|
|
441
|
+
return;
|
|
442
|
+
// Calculate info file checksum
|
|
443
|
+
const infoContent = await this.storage.getRubyGemsInfo(gemName) || '';
|
|
444
|
+
const infoChecksum = await helpers.calculateMD5(infoContent);
|
|
445
|
+
const updated = helpers.updateCompactIndexVersions(existingVersions, gemName, { version, platform: platform !== 'ruby' ? platform : undefined, yanked }, infoChecksum);
|
|
446
|
+
await this.storage.putRubyGemsVersions(updated);
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
449
|
+
* Update names file with new gem
|
|
450
|
+
*/
|
|
451
|
+
async updateNamesFile(gemName) {
|
|
452
|
+
const existingNames = await this.storage.getRubyGemsNames();
|
|
453
|
+
if (!existingNames)
|
|
454
|
+
return;
|
|
455
|
+
const lines = existingNames.split('\n').filter(l => l !== '---');
|
|
456
|
+
if (!lines.includes(gemName)) {
|
|
457
|
+
lines.push(gemName);
|
|
458
|
+
lines.sort();
|
|
459
|
+
const updated = helpers.generateNamesFile(lines);
|
|
460
|
+
await this.storage.putRubyGemsNames(updated);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* Helper: Create error response
|
|
465
|
+
*/
|
|
466
|
+
errorResponse(status, message) {
|
|
467
|
+
const error = { message, status };
|
|
468
|
+
return {
|
|
469
|
+
status,
|
|
470
|
+
headers: { 'Content-Type': 'application/json' },
|
|
471
|
+
body: Buffer.from(JSON.stringify(error)),
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5ydWJ5Z2Vtc3JlZ2lzdHJ5LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdHMvcnVieWdlbXMvY2xhc3Nlcy5ydWJ5Z2Vtc3JlZ2lzdHJ5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQUNoRCxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFDL0QsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLG9DQUFvQyxDQUFDO0FBQ3JFLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQVU3RCxPQUFPLEtBQUssT0FBTyxNQUFNLHVCQUF1QixDQUFDO0FBRWpEOzs7R0FHRztBQUNILE1BQU0sT0FBTyxnQkFBaUIsU0FBUSxZQUFZO0lBQ3hDLE9BQU8sQ0FBa0I7SUFDekIsV0FBVyxDQUFjO0lBQ3pCLFFBQVEsR0FBVyxXQUFXLENBQUM7SUFDL0IsV0FBVyxDQUFTO0lBQ3BCLE1BQU0sQ0FBVztJQUV6QixZQUNFLE9BQXdCLEVBQ3hCLFdBQXdCLEVBQ3hCLFdBQW1CLFdBQVcsRUFDOUIsY0FBc0IsZ0NBQWdDO1FBRXRELEtBQUssRUFBRSxDQUFDO1FBQ1IsSUFBSSxDQUFDLE9BQU8sR0FBRyxPQUFPLENBQUM7UUFDdkIsSUFBSSxDQUFDLFdBQVcsR0FBRyxXQUFXLENBQUM7UUFDL0IsSUFBSSxDQUFDLFFBQVEsR0FBRyxRQUFRLENBQUM7UUFDekIsSUFBSSxDQUFDLFdBQVcsR0FBRyxXQUFXLENBQUM7UUFFL0Isb0JBQW9CO1FBQ3BCLElBQUksQ0FBQyxNQUFNLEdBQUcsSUFBSSxRQUFRLENBQUM7WUFDekIsVUFBVSxFQUFFO2dCQUNWLE9BQU8sRUFBRSxZQUFZO2dCQUNyQixXQUFXLEVBQUUsZUFBZTtnQkFDNUIsYUFBYSxFQUFFLG1CQUFtQjtnQkFDbEMsV0FBVyxFQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsUUFB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
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helper functions for RubyGems registry
|
|
3
|
+
* Compact Index generation, dependency formatting, etc.
|
|
4
|
+
*/
|
|
5
|
+
import type { IRubyGemsDependency, ICompactIndexVersionsEntry, ICompactIndexInfoEntry } from './interfaces.rubygems.js';
|
|
6
|
+
/**
|
|
7
|
+
* Generate Compact Index versions file
|
|
8
|
+
* Format: GEMNAME [-]VERSION_PLATFORM[,VERSION_PLATFORM,...] MD5
|
|
9
|
+
* @param entries - Version entries for all gems
|
|
10
|
+
* @returns Compact Index versions file content
|
|
11
|
+
*/
|
|
12
|
+
export declare function generateCompactIndexVersions(entries: ICompactIndexVersionsEntry[]): string;
|
|
13
|
+
/**
|
|
14
|
+
* Generate Compact Index info file for a gem
|
|
15
|
+
* Format: VERSION[-PLATFORM] [DEP[,DEP,...]]|REQ[,REQ,...]
|
|
16
|
+
* @param entries - Info entries for gem versions
|
|
17
|
+
* @returns Compact Index info file content
|
|
18
|
+
*/
|
|
19
|
+
export declare function generateCompactIndexInfo(entries: ICompactIndexInfoEntry[]): string;
|
|
20
|
+
/**
|
|
21
|
+
* Format a dependency for Compact Index
|
|
22
|
+
* Format: GEM:CONSTRAINT[&CONSTRAINT]
|
|
23
|
+
* @param dep - Dependency object
|
|
24
|
+
* @returns Formatted dependency string
|
|
25
|
+
*/
|
|
26
|
+
export declare function formatDependency(dep: IRubyGemsDependency): string;
|
|
27
|
+
/**
|
|
28
|
+
* Parse dependency string from Compact Index
|
|
29
|
+
* @param depStr - Dependency string
|
|
30
|
+
* @returns Dependency object
|
|
31
|
+
*/
|
|
32
|
+
export declare function parseDependency(depStr: string): IRubyGemsDependency;
|
|
33
|
+
/**
|
|
34
|
+
* Generate names file (newline-separated gem names)
|
|
35
|
+
* @param names - List of gem names
|
|
36
|
+
* @returns Names file content
|
|
37
|
+
*/
|
|
38
|
+
export declare function generateNamesFile(names: string[]): string;
|
|
39
|
+
/**
|
|
40
|
+
* Calculate MD5 hash for Compact Index checksum
|
|
41
|
+
* @param content - Content to hash
|
|
42
|
+
* @returns MD5 hash (hex)
|
|
43
|
+
*/
|
|
44
|
+
export declare function calculateMD5(content: string): Promise<string>;
|
|
45
|
+
/**
|
|
46
|
+
* Calculate SHA256 hash for gem files
|
|
47
|
+
* @param data - Data to hash
|
|
48
|
+
* @returns SHA256 hash (hex)
|
|
49
|
+
*/
|
|
50
|
+
export declare function calculateSHA256(data: Buffer): Promise<string>;
|
|
51
|
+
/**
|
|
52
|
+
* Parse gem filename to extract name, version, and platform
|
|
53
|
+
* @param filename - Gem filename (e.g., "rails-7.0.0-x86_64-linux.gem")
|
|
54
|
+
* @returns Parsed info or null
|
|
55
|
+
*/
|
|
56
|
+
export declare function parseGemFilename(filename: string): {
|
|
57
|
+
name: string;
|
|
58
|
+
version: string;
|
|
59
|
+
platform?: string;
|
|
60
|
+
} | null;
|
|
61
|
+
/**
|
|
62
|
+
* Validate gem name
|
|
63
|
+
* Must contain only ASCII letters, numbers, _, and -
|
|
64
|
+
* @param name - Gem name
|
|
65
|
+
* @returns true if valid
|
|
66
|
+
*/
|
|
67
|
+
export declare function isValidGemName(name: string): boolean;
|
|
68
|
+
/**
|
|
69
|
+
* Validate version string
|
|
70
|
+
* Basic semantic versioning check
|
|
71
|
+
* @param version - Version string
|
|
72
|
+
* @returns true if valid
|
|
73
|
+
*/
|
|
74
|
+
export declare function isValidVersion(version: string): boolean;
|
|
75
|
+
/**
|
|
76
|
+
* Build version list entry for Compact Index
|
|
77
|
+
* @param versions - Version info
|
|
78
|
+
* @returns Version list string
|
|
79
|
+
*/
|
|
80
|
+
export declare function buildVersionList(versions: Array<{
|
|
81
|
+
version: string;
|
|
82
|
+
platform?: string;
|
|
83
|
+
yanked: boolean;
|
|
84
|
+
}>): string;
|
|
85
|
+
/**
|
|
86
|
+
* Parse version list from Compact Index
|
|
87
|
+
* @param versionStr - Version list string
|
|
88
|
+
* @returns Parsed versions
|
|
89
|
+
*/
|
|
90
|
+
export declare function parseVersionList(versionStr: string): Array<{
|
|
91
|
+
version: string;
|
|
92
|
+
platform?: string;
|
|
93
|
+
yanked: boolean;
|
|
94
|
+
}>;
|
|
95
|
+
/**
|
|
96
|
+
* Generate JSON response for /api/v1/versions/{gem}.json
|
|
97
|
+
* @param gemName - Gem name
|
|
98
|
+
* @param versions - Version list
|
|
99
|
+
* @returns JSON response object
|
|
100
|
+
*/
|
|
101
|
+
export declare function generateVersionsJson(gemName: string, versions: Array<{
|
|
102
|
+
version: string;
|
|
103
|
+
platform?: string;
|
|
104
|
+
uploadTime?: string;
|
|
105
|
+
}>): any;
|
|
106
|
+
/**
|
|
107
|
+
* Generate JSON response for /api/v1/dependencies
|
|
108
|
+
* @param gems - Map of gem names to version dependencies
|
|
109
|
+
* @returns JSON response array
|
|
110
|
+
*/
|
|
111
|
+
export declare function generateDependenciesJson(gems: Map<string, Array<{
|
|
112
|
+
version: string;
|
|
113
|
+
platform?: string;
|
|
114
|
+
dependencies: IRubyGemsDependency[];
|
|
115
|
+
}>>): any;
|
|
116
|
+
/**
|
|
117
|
+
* Update Compact Index versions file with new gem version
|
|
118
|
+
* Handles append-only semantics for the current month
|
|
119
|
+
* @param existingContent - Current versions file content
|
|
120
|
+
* @param gemName - Gem name
|
|
121
|
+
* @param newVersion - New version info
|
|
122
|
+
* @param infoChecksum - MD5 of info file
|
|
123
|
+
* @returns Updated versions file content
|
|
124
|
+
*/
|
|
125
|
+
export declare function updateCompactIndexVersions(existingContent: string, gemName: string, newVersion: {
|
|
126
|
+
version: string;
|
|
127
|
+
platform?: string;
|
|
128
|
+
yanked: boolean;
|
|
129
|
+
}, infoChecksum: string): string;
|
|
130
|
+
/**
|
|
131
|
+
* Update Compact Index info file with new version
|
|
132
|
+
* @param existingContent - Current info file content
|
|
133
|
+
* @param newEntry - New version entry
|
|
134
|
+
* @returns Updated info file content
|
|
135
|
+
*/
|
|
136
|
+
export declare function updateCompactIndexInfo(existingContent: string, newEntry: ICompactIndexInfoEntry): string;
|
|
137
|
+
/**
|
|
138
|
+
* Extract gem specification from .gem file
|
|
139
|
+
* Note: This is a simplified version. Full implementation would use tar + gzip + Marshal
|
|
140
|
+
* @param gemData - Gem file data
|
|
141
|
+
* @returns Extracted spec or null
|
|
142
|
+
*/
|
|
143
|
+
export declare function extractGemSpec(gemData: Buffer): Promise<any | null>;
|