@push.rocks/smartregistry 1.4.0 → 1.5.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 +1 -1
- package/dist_ts/composer/classes.composerregistry.js +3 -17
- 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 +68 -0
- package/dist_ts/core/classes.registrystorage.js +195 -1
- package/dist_ts/core/interfaces.core.d.ts +13 -1
- package/dist_ts/pypi/classes.pypiregistry.d.ts +70 -0
- package/dist_ts/pypi/classes.pypiregistry.js +470 -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/package.json +1 -1
- package/readme.hints.md +333 -1
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/composer/classes.composerregistry.ts +2 -18
- package/ts/core/classes.authmanager.ts +161 -2
- package/ts/core/classes.registrystorage.ts +227 -0
- package/ts/core/interfaces.core.ts +13 -1
- package/ts/pypi/classes.pypiregistry.ts +564 -0
- package/ts/pypi/helpers.pypi.ts +299 -0
- package/ts/pypi/index.ts +8 -0
- package/ts/pypi/interfaces.pypi.ts +316 -0
|
@@ -0,0 +1,470 @@
|
|
|
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.pypi.js';
|
|
6
|
+
/**
|
|
7
|
+
* PyPI registry implementation
|
|
8
|
+
* Implements PEP 503 (Simple API), PEP 691 (JSON API), and legacy upload API
|
|
9
|
+
*/
|
|
10
|
+
export class PypiRegistry extends BaseRegistry {
|
|
11
|
+
storage;
|
|
12
|
+
authManager;
|
|
13
|
+
basePath = '/pypi';
|
|
14
|
+
registryUrl;
|
|
15
|
+
logger;
|
|
16
|
+
constructor(storage, authManager, basePath = '/pypi', registryUrl = 'http://localhost:5000') {
|
|
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: 'pypi-registry',
|
|
28
|
+
environment: process.env.NODE_ENV || 'development',
|
|
29
|
+
runtime: 'node',
|
|
30
|
+
zone: 'pypi'
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
this.logger.enableConsole();
|
|
34
|
+
}
|
|
35
|
+
async init() {
|
|
36
|
+
// Initialize root Simple API index if not exists
|
|
37
|
+
const existingIndex = await this.storage.getPypiSimpleRootIndex();
|
|
38
|
+
if (!existingIndex) {
|
|
39
|
+
const html = helpers.generateSimpleRootHtml([]);
|
|
40
|
+
await this.storage.putPypiSimpleRootIndex(html);
|
|
41
|
+
this.logger.log('info', 'Initialized PyPI root index');
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
getBasePath() {
|
|
45
|
+
return this.basePath;
|
|
46
|
+
}
|
|
47
|
+
async handleRequest(context) {
|
|
48
|
+
let path = context.path.replace(this.basePath, '');
|
|
49
|
+
// Also handle /simple path prefix
|
|
50
|
+
if (path.startsWith('/simple')) {
|
|
51
|
+
path = path.replace('/simple', '');
|
|
52
|
+
return this.handleSimpleRequest(path, context);
|
|
53
|
+
}
|
|
54
|
+
// Extract token (Basic Auth or Bearer)
|
|
55
|
+
const token = await this.extractToken(context);
|
|
56
|
+
this.logger.log('debug', `handleRequest: ${context.method} ${path}`, {
|
|
57
|
+
method: context.method,
|
|
58
|
+
path,
|
|
59
|
+
hasAuth: !!token
|
|
60
|
+
});
|
|
61
|
+
// Root upload endpoint (POST /)
|
|
62
|
+
if ((path === '/' || path === '') && context.method === 'POST') {
|
|
63
|
+
return this.handleUpload(context, token);
|
|
64
|
+
}
|
|
65
|
+
// Package metadata JSON API: GET /pypi/{package}/json
|
|
66
|
+
const jsonMatch = path.match(/^\/pypi\/([^\/]+)\/json$/);
|
|
67
|
+
if (jsonMatch && context.method === 'GET') {
|
|
68
|
+
return this.handlePackageJson(jsonMatch[1]);
|
|
69
|
+
}
|
|
70
|
+
// Version-specific JSON API: GET /pypi/{package}/{version}/json
|
|
71
|
+
const versionJsonMatch = path.match(/^\/pypi\/([^\/]+)\/([^\/]+)\/json$/);
|
|
72
|
+
if (versionJsonMatch && context.method === 'GET') {
|
|
73
|
+
return this.handleVersionJson(versionJsonMatch[1], versionJsonMatch[2]);
|
|
74
|
+
}
|
|
75
|
+
// Package file download: GET /packages/{package}/{filename}
|
|
76
|
+
const downloadMatch = path.match(/^\/packages\/([^\/]+)\/(.+)$/);
|
|
77
|
+
if (downloadMatch && context.method === 'GET') {
|
|
78
|
+
return this.handleDownload(downloadMatch[1], downloadMatch[2]);
|
|
79
|
+
}
|
|
80
|
+
// Delete package: DELETE /packages/{package}
|
|
81
|
+
if (path.match(/^\/packages\/([^\/]+)$/) && context.method === 'DELETE') {
|
|
82
|
+
const packageName = path.match(/^\/packages\/([^\/]+)$/)?.[1];
|
|
83
|
+
return this.handleDeletePackage(packageName, token);
|
|
84
|
+
}
|
|
85
|
+
// Delete version: DELETE /packages/{package}/{version}
|
|
86
|
+
const deleteVersionMatch = path.match(/^\/packages\/([^\/]+)\/([^\/]+)$/);
|
|
87
|
+
if (deleteVersionMatch && context.method === 'DELETE') {
|
|
88
|
+
return this.handleDeleteVersion(deleteVersionMatch[1], deleteVersionMatch[2], token);
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
status: 404,
|
|
92
|
+
headers: { 'Content-Type': 'application/json' },
|
|
93
|
+
body: Buffer.from(JSON.stringify({ message: 'Not Found' })),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Check if token has permission for resource
|
|
98
|
+
*/
|
|
99
|
+
async checkPermission(token, resource, action) {
|
|
100
|
+
if (!token)
|
|
101
|
+
return false;
|
|
102
|
+
return this.authManager.authorize(token, `pypi:package:${resource}`, action);
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Handle Simple API requests (PEP 503 HTML or PEP 691 JSON)
|
|
106
|
+
*/
|
|
107
|
+
async handleSimpleRequest(path, context) {
|
|
108
|
+
// Ensure path ends with / (PEP 503 requirement)
|
|
109
|
+
if (!path.endsWith('/') && !path.includes('.')) {
|
|
110
|
+
return {
|
|
111
|
+
status: 301,
|
|
112
|
+
headers: { 'Location': `${this.basePath}/simple${path}/` },
|
|
113
|
+
body: Buffer.from(''),
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
// Root index: /simple/
|
|
117
|
+
if (path === '/' || path === '') {
|
|
118
|
+
return this.handleSimpleRoot(context);
|
|
119
|
+
}
|
|
120
|
+
// Package index: /simple/{package}/
|
|
121
|
+
const packageMatch = path.match(/^\/([^\/]+)\/$/);
|
|
122
|
+
if (packageMatch) {
|
|
123
|
+
return this.handleSimplePackage(packageMatch[1], context);
|
|
124
|
+
}
|
|
125
|
+
return {
|
|
126
|
+
status: 404,
|
|
127
|
+
headers: { 'Content-Type': 'text/html; charset=utf-8' },
|
|
128
|
+
body: Buffer.from('<html><body><h1>404 Not Found</h1></body></html>'),
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Handle Simple API root index
|
|
133
|
+
* Returns HTML (PEP 503) or JSON (PEP 691) based on Accept header
|
|
134
|
+
*/
|
|
135
|
+
async handleSimpleRoot(context) {
|
|
136
|
+
const acceptHeader = context.headers['accept'] || context.headers['Accept'] || '';
|
|
137
|
+
const preferJson = acceptHeader.includes('application/vnd.pypi.simple') &&
|
|
138
|
+
acceptHeader.includes('json');
|
|
139
|
+
const packages = await this.storage.listPypiPackages();
|
|
140
|
+
if (preferJson) {
|
|
141
|
+
// PEP 691: JSON response
|
|
142
|
+
const response = helpers.generateJsonRootResponse(packages);
|
|
143
|
+
return {
|
|
144
|
+
status: 200,
|
|
145
|
+
headers: {
|
|
146
|
+
'Content-Type': 'application/vnd.pypi.simple.v1+json',
|
|
147
|
+
'Cache-Control': 'public, max-age=600'
|
|
148
|
+
},
|
|
149
|
+
body: Buffer.from(JSON.stringify(response)),
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
// PEP 503: HTML response
|
|
154
|
+
const html = helpers.generateSimpleRootHtml(packages);
|
|
155
|
+
// Update stored index
|
|
156
|
+
await this.storage.putPypiSimpleRootIndex(html);
|
|
157
|
+
return {
|
|
158
|
+
status: 200,
|
|
159
|
+
headers: {
|
|
160
|
+
'Content-Type': 'text/html; charset=utf-8',
|
|
161
|
+
'Cache-Control': 'public, max-age=600'
|
|
162
|
+
},
|
|
163
|
+
body: Buffer.from(html),
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Handle Simple API package index
|
|
169
|
+
* Returns HTML (PEP 503) or JSON (PEP 691) based on Accept header
|
|
170
|
+
*/
|
|
171
|
+
async handleSimplePackage(packageName, context) {
|
|
172
|
+
const normalized = helpers.normalizePypiPackageName(packageName);
|
|
173
|
+
// Get package metadata
|
|
174
|
+
const metadata = await this.storage.getPypiPackageMetadata(normalized);
|
|
175
|
+
if (!metadata) {
|
|
176
|
+
return {
|
|
177
|
+
status: 404,
|
|
178
|
+
headers: { 'Content-Type': 'text/html; charset=utf-8' },
|
|
179
|
+
body: Buffer.from('<html><body><h1>404 Not Found</h1></body></html>'),
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
// Build file list from all versions
|
|
183
|
+
const files = [];
|
|
184
|
+
for (const [version, versionMeta] of Object.entries(metadata.versions || {})) {
|
|
185
|
+
for (const file of versionMeta.files || []) {
|
|
186
|
+
files.push({
|
|
187
|
+
filename: file.filename,
|
|
188
|
+
url: `${this.registryUrl}/pypi/packages/${normalized}/${file.filename}`,
|
|
189
|
+
hashes: file.hashes,
|
|
190
|
+
'requires-python': file['requires-python'],
|
|
191
|
+
yanked: file.yanked || versionMeta.yanked,
|
|
192
|
+
size: file.size,
|
|
193
|
+
'upload-time': file['upload-time'],
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
const acceptHeader = context.headers['accept'] || context.headers['Accept'] || '';
|
|
198
|
+
const preferJson = acceptHeader.includes('application/vnd.pypi.simple') &&
|
|
199
|
+
acceptHeader.includes('json');
|
|
200
|
+
if (preferJson) {
|
|
201
|
+
// PEP 691: JSON response
|
|
202
|
+
const response = helpers.generateJsonPackageResponse(normalized, files);
|
|
203
|
+
return {
|
|
204
|
+
status: 200,
|
|
205
|
+
headers: {
|
|
206
|
+
'Content-Type': 'application/vnd.pypi.simple.v1+json',
|
|
207
|
+
'Cache-Control': 'public, max-age=300'
|
|
208
|
+
},
|
|
209
|
+
body: Buffer.from(JSON.stringify(response)),
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
// PEP 503: HTML response
|
|
214
|
+
const html = helpers.generateSimplePackageHtml(normalized, files, this.registryUrl);
|
|
215
|
+
// Update stored index
|
|
216
|
+
await this.storage.putPypiSimpleIndex(normalized, html);
|
|
217
|
+
return {
|
|
218
|
+
status: 200,
|
|
219
|
+
headers: {
|
|
220
|
+
'Content-Type': 'text/html; charset=utf-8',
|
|
221
|
+
'Cache-Control': 'public, max-age=300'
|
|
222
|
+
},
|
|
223
|
+
body: Buffer.from(html),
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Extract authentication token from request
|
|
229
|
+
*/
|
|
230
|
+
async extractToken(context) {
|
|
231
|
+
const authHeader = context.headers['authorization'] || context.headers['Authorization'];
|
|
232
|
+
if (!authHeader)
|
|
233
|
+
return null;
|
|
234
|
+
// Handle Basic Auth (username:password or __token__:token)
|
|
235
|
+
if (authHeader.startsWith('Basic ')) {
|
|
236
|
+
const base64 = authHeader.substring(6);
|
|
237
|
+
const decoded = Buffer.from(base64, 'base64').toString('utf-8');
|
|
238
|
+
const [username, password] = decoded.split(':');
|
|
239
|
+
// PyPI token authentication: username = __token__
|
|
240
|
+
if (username === '__token__') {
|
|
241
|
+
return this.authManager.validateToken(password, 'pypi');
|
|
242
|
+
}
|
|
243
|
+
// Username/password authentication (would need user lookup)
|
|
244
|
+
// For now, not implemented
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
247
|
+
// Handle Bearer token
|
|
248
|
+
if (authHeader.startsWith('Bearer ')) {
|
|
249
|
+
const token = authHeader.substring(7);
|
|
250
|
+
return this.authManager.validateToken(token, 'pypi');
|
|
251
|
+
}
|
|
252
|
+
return null;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Handle package upload (multipart/form-data)
|
|
256
|
+
* POST / with :action=file_upload
|
|
257
|
+
*/
|
|
258
|
+
async handleUpload(context, token) {
|
|
259
|
+
if (!token) {
|
|
260
|
+
return {
|
|
261
|
+
status: 401,
|
|
262
|
+
headers: {
|
|
263
|
+
'Content-Type': 'application/json',
|
|
264
|
+
'WWW-Authenticate': 'Basic realm="PyPI"'
|
|
265
|
+
},
|
|
266
|
+
body: Buffer.from(JSON.stringify({ message: 'Authentication required' })),
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
try {
|
|
270
|
+
// Parse multipart form data (context.body should be parsed by server)
|
|
271
|
+
const formData = context.body; // Assuming parsed multipart data
|
|
272
|
+
if (!formData || formData[':action'] !== 'file_upload') {
|
|
273
|
+
return this.errorResponse(400, 'Invalid upload request');
|
|
274
|
+
}
|
|
275
|
+
// Extract required fields
|
|
276
|
+
const packageName = formData.name;
|
|
277
|
+
const version = formData.version;
|
|
278
|
+
const filename = formData.content?.filename;
|
|
279
|
+
const fileData = formData.content?.data;
|
|
280
|
+
const filetype = formData.filetype; // 'bdist_wheel' or 'sdist'
|
|
281
|
+
const pyversion = formData.pyversion;
|
|
282
|
+
if (!packageName || !version || !filename || !fileData) {
|
|
283
|
+
return this.errorResponse(400, 'Missing required fields');
|
|
284
|
+
}
|
|
285
|
+
// Validate package name
|
|
286
|
+
if (!helpers.isValidPackageName(packageName)) {
|
|
287
|
+
return this.errorResponse(400, 'Invalid package name');
|
|
288
|
+
}
|
|
289
|
+
const normalized = helpers.normalizePypiPackageName(packageName);
|
|
290
|
+
// Check permission
|
|
291
|
+
if (!(await this.checkPermission(token, normalized, 'write'))) {
|
|
292
|
+
return this.errorResponse(403, 'Insufficient permissions');
|
|
293
|
+
}
|
|
294
|
+
// Calculate hashes
|
|
295
|
+
const hashes = {};
|
|
296
|
+
if (formData.sha256_digest) {
|
|
297
|
+
hashes.sha256 = formData.sha256_digest;
|
|
298
|
+
}
|
|
299
|
+
else {
|
|
300
|
+
hashes.sha256 = await helpers.calculateHash(fileData, 'sha256');
|
|
301
|
+
}
|
|
302
|
+
if (formData.md5_digest) {
|
|
303
|
+
// MD5 digest in PyPI is urlsafe base64, convert to hex
|
|
304
|
+
hashes.md5 = await helpers.calculateHash(fileData, 'md5');
|
|
305
|
+
}
|
|
306
|
+
if (formData.blake2_256_digest) {
|
|
307
|
+
hashes.blake2b = formData.blake2_256_digest;
|
|
308
|
+
}
|
|
309
|
+
// Store file
|
|
310
|
+
await this.storage.putPypiPackageFile(normalized, filename, fileData);
|
|
311
|
+
// Update metadata
|
|
312
|
+
let metadata = await this.storage.getPypiPackageMetadata(normalized);
|
|
313
|
+
if (!metadata) {
|
|
314
|
+
metadata = {
|
|
315
|
+
name: normalized,
|
|
316
|
+
versions: {},
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
if (!metadata.versions[version]) {
|
|
320
|
+
metadata.versions[version] = {
|
|
321
|
+
version,
|
|
322
|
+
files: [],
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
// Add file to version
|
|
326
|
+
metadata.versions[version].files.push({
|
|
327
|
+
filename,
|
|
328
|
+
path: `pypi/packages/${normalized}/${filename}`,
|
|
329
|
+
filetype,
|
|
330
|
+
python_version: pyversion,
|
|
331
|
+
hashes,
|
|
332
|
+
size: fileData.length,
|
|
333
|
+
'requires-python': formData.requires_python,
|
|
334
|
+
'upload-time': new Date().toISOString(),
|
|
335
|
+
'uploaded-by': token.userId,
|
|
336
|
+
});
|
|
337
|
+
// Store core metadata if provided
|
|
338
|
+
if (formData.summary || formData.description) {
|
|
339
|
+
metadata.versions[version].metadata = helpers.extractCoreMetadata(formData);
|
|
340
|
+
}
|
|
341
|
+
metadata['last-modified'] = new Date().toISOString();
|
|
342
|
+
await this.storage.putPypiPackageMetadata(normalized, metadata);
|
|
343
|
+
this.logger.log('info', `Package uploaded: ${normalized} ${version}`, {
|
|
344
|
+
filename,
|
|
345
|
+
size: fileData.length
|
|
346
|
+
});
|
|
347
|
+
return {
|
|
348
|
+
status: 200,
|
|
349
|
+
headers: { 'Content-Type': 'application/json' },
|
|
350
|
+
body: Buffer.from(JSON.stringify({
|
|
351
|
+
message: 'Package uploaded successfully',
|
|
352
|
+
url: `${this.registryUrl}/pypi/packages/${normalized}/${filename}`
|
|
353
|
+
})),
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
catch (error) {
|
|
357
|
+
this.logger.log('error', 'Upload failed', { error: error.message });
|
|
358
|
+
return this.errorResponse(500, 'Upload failed: ' + error.message);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Handle package download
|
|
363
|
+
*/
|
|
364
|
+
async handleDownload(packageName, filename) {
|
|
365
|
+
const normalized = helpers.normalizePypiPackageName(packageName);
|
|
366
|
+
const fileData = await this.storage.getPypiPackageFile(normalized, filename);
|
|
367
|
+
if (!fileData) {
|
|
368
|
+
return {
|
|
369
|
+
status: 404,
|
|
370
|
+
headers: { 'Content-Type': 'application/json' },
|
|
371
|
+
body: Buffer.from(JSON.stringify({ message: 'File not found' })),
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
return {
|
|
375
|
+
status: 200,
|
|
376
|
+
headers: {
|
|
377
|
+
'Content-Type': 'application/octet-stream',
|
|
378
|
+
'Content-Disposition': `attachment; filename="${filename}"`,
|
|
379
|
+
'Content-Length': fileData.length.toString()
|
|
380
|
+
},
|
|
381
|
+
body: fileData,
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* Handle package JSON API (all versions)
|
|
386
|
+
*/
|
|
387
|
+
async handlePackageJson(packageName) {
|
|
388
|
+
const normalized = helpers.normalizePypiPackageName(packageName);
|
|
389
|
+
const metadata = await this.storage.getPypiPackageMetadata(normalized);
|
|
390
|
+
if (!metadata) {
|
|
391
|
+
return this.errorResponse(404, 'Package not found');
|
|
392
|
+
}
|
|
393
|
+
return {
|
|
394
|
+
status: 200,
|
|
395
|
+
headers: {
|
|
396
|
+
'Content-Type': 'application/json',
|
|
397
|
+
'Cache-Control': 'public, max-age=300'
|
|
398
|
+
},
|
|
399
|
+
body: Buffer.from(JSON.stringify(metadata)),
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Handle version-specific JSON API
|
|
404
|
+
*/
|
|
405
|
+
async handleVersionJson(packageName, version) {
|
|
406
|
+
const normalized = helpers.normalizePypiPackageName(packageName);
|
|
407
|
+
const metadata = await this.storage.getPypiPackageMetadata(normalized);
|
|
408
|
+
if (!metadata || !metadata.versions[version]) {
|
|
409
|
+
return this.errorResponse(404, 'Version not found');
|
|
410
|
+
}
|
|
411
|
+
return {
|
|
412
|
+
status: 200,
|
|
413
|
+
headers: {
|
|
414
|
+
'Content-Type': 'application/json',
|
|
415
|
+
'Cache-Control': 'public, max-age=300'
|
|
416
|
+
},
|
|
417
|
+
body: Buffer.from(JSON.stringify(metadata.versions[version])),
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* Handle package deletion
|
|
422
|
+
*/
|
|
423
|
+
async handleDeletePackage(packageName, token) {
|
|
424
|
+
if (!token) {
|
|
425
|
+
return this.errorResponse(401, 'Authentication required');
|
|
426
|
+
}
|
|
427
|
+
const normalized = helpers.normalizePypiPackageName(packageName);
|
|
428
|
+
if (!(await this.checkPermission(token, normalized, 'delete'))) {
|
|
429
|
+
return this.errorResponse(403, 'Insufficient permissions');
|
|
430
|
+
}
|
|
431
|
+
await this.storage.deletePypiPackage(normalized);
|
|
432
|
+
this.logger.log('info', `Package deleted: ${normalized}`);
|
|
433
|
+
return {
|
|
434
|
+
status: 204,
|
|
435
|
+
headers: {},
|
|
436
|
+
body: Buffer.from(''),
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* Handle version deletion
|
|
441
|
+
*/
|
|
442
|
+
async handleDeleteVersion(packageName, version, token) {
|
|
443
|
+
if (!token) {
|
|
444
|
+
return this.errorResponse(401, 'Authentication required');
|
|
445
|
+
}
|
|
446
|
+
const normalized = helpers.normalizePypiPackageName(packageName);
|
|
447
|
+
if (!(await this.checkPermission(token, normalized, 'delete'))) {
|
|
448
|
+
return this.errorResponse(403, 'Insufficient permissions');
|
|
449
|
+
}
|
|
450
|
+
await this.storage.deletePypiPackageVersion(normalized, version);
|
|
451
|
+
this.logger.log('info', `Version deleted: ${normalized} ${version}`);
|
|
452
|
+
return {
|
|
453
|
+
status: 204,
|
|
454
|
+
headers: {},
|
|
455
|
+
body: Buffer.from(''),
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
/**
|
|
459
|
+
* Helper: Create error response
|
|
460
|
+
*/
|
|
461
|
+
errorResponse(status, message) {
|
|
462
|
+
const error = { message, status };
|
|
463
|
+
return {
|
|
464
|
+
status,
|
|
465
|
+
headers: { 'Content-Type': 'application/json' },
|
|
466
|
+
body: Buffer.from(JSON.stringify(error)),
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5weXBpcmVnaXN0cnkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9weXBpL2NsYXNzZXMucHlwaXJlZ2lzdHJ5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQUNoRCxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFDL0QsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLG9DQUFvQyxDQUFDO0FBQ3JFLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQVE3RCxPQUFPLEtBQUssT0FBTyxNQUFNLG1CQUFtQixDQUFDO0FBRTdDOzs7R0FHRztBQUNILE1BQU0sT0FBTyxZQUFhLFNBQVEsWUFBWTtJQUNwQyxPQUFPLENBQWtCO0lBQ3pCLFdBQVcsQ0FBYztJQUN6QixRQUFRLEdBQVcsT0FBTyxDQUFDO0lBQzNCLFdBQVcsQ0FBUztJQUNwQixNQUFNLENBQVc7SUFFekIsWUFDRSxPQUF3QixFQUN4QixXQUF3QixFQUN4QixXQUFtQixPQUFPLEVBQzFCLGNBQXNCLHVCQUF1QjtRQUU3QyxLQUFLLEVBQUUsQ0FBQztRQUNSLElBQUksQ0FBQyxPQUFPLEdBQUcsT0FBTyxDQUFDO1FBQ3ZCLElBQUksQ0FBQyxXQUFXLEdBQUcsV0FBVyxDQUFDO1FBQy9CLElBQUksQ0FBQyxRQUFRLEdBQUcsUUFBUSxDQUFDO1FBQ3pCLElBQUksQ0FBQyxXQUFXLEdBQUcsV0FBVyxDQUFDO1FBRS9CLG9CQUFvQjtRQUNwQixJQUFJLENBQUMsTUFBTSxHQUFHLElBQUksUUFBUSxDQUFDO1lBQ3pCLFVBQVUsRUFBRTtnQkFDVixPQUFPLEVBQUUsWUFBWTtnQkFDckIsV0FBVyxFQUFFLGVBQWU7Z0JBQzVCLGFBQWEsRUFBRSxlQUFlO2dCQUM5QixXQUFXLEVBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQyxRQUFnQixJQUFJLGFBQWE7Z0JB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
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helper functions for PyPI registry
|
|
3
|
+
* Package name normalization, HTML generation, etc.
|
|
4
|
+
*/
|
|
5
|
+
import type { IPypiFile } from './interfaces.pypi.js';
|
|
6
|
+
/**
|
|
7
|
+
* Normalize package name according to PEP 503
|
|
8
|
+
* Lowercase and replace runs of [._-] with a single dash
|
|
9
|
+
* @param name - Package name
|
|
10
|
+
* @returns Normalized name
|
|
11
|
+
*/
|
|
12
|
+
export declare function normalizePypiPackageName(name: string): string;
|
|
13
|
+
/**
|
|
14
|
+
* Escape HTML special characters to prevent XSS
|
|
15
|
+
* @param str - String to escape
|
|
16
|
+
* @returns Escaped string
|
|
17
|
+
*/
|
|
18
|
+
export declare function escapeHtml(str: string): string;
|
|
19
|
+
/**
|
|
20
|
+
* Generate PEP 503 compliant HTML for root index (all packages)
|
|
21
|
+
* @param packages - List of package names
|
|
22
|
+
* @returns HTML string
|
|
23
|
+
*/
|
|
24
|
+
export declare function generateSimpleRootHtml(packages: string[]): string;
|
|
25
|
+
/**
|
|
26
|
+
* Generate PEP 503 compliant HTML for package index (file list)
|
|
27
|
+
* @param packageName - Package name (normalized)
|
|
28
|
+
* @param files - List of files
|
|
29
|
+
* @param baseUrl - Base URL for downloads
|
|
30
|
+
* @returns HTML string
|
|
31
|
+
*/
|
|
32
|
+
export declare function generateSimplePackageHtml(packageName: string, files: IPypiFile[], baseUrl: string): string;
|
|
33
|
+
/**
|
|
34
|
+
* Parse filename to extract package info
|
|
35
|
+
* Supports wheel and sdist formats
|
|
36
|
+
* @param filename - Package filename
|
|
37
|
+
* @returns Parsed info or null
|
|
38
|
+
*/
|
|
39
|
+
export declare function parsePackageFilename(filename: string): {
|
|
40
|
+
name: string;
|
|
41
|
+
version: string;
|
|
42
|
+
filetype: 'bdist_wheel' | 'sdist';
|
|
43
|
+
pythonVersion?: string;
|
|
44
|
+
} | null;
|
|
45
|
+
/**
|
|
46
|
+
* Calculate hash digest for a buffer
|
|
47
|
+
* @param data - Data to hash
|
|
48
|
+
* @param algorithm - Hash algorithm (sha256, md5, blake2b)
|
|
49
|
+
* @returns Hex-encoded hash
|
|
50
|
+
*/
|
|
51
|
+
export declare function calculateHash(data: Buffer, algorithm: 'sha256' | 'md5' | 'blake2b'): Promise<string>;
|
|
52
|
+
/**
|
|
53
|
+
* Validate package name
|
|
54
|
+
* Must contain only ASCII letters, numbers, ., -, and _
|
|
55
|
+
* @param name - Package name
|
|
56
|
+
* @returns true if valid
|
|
57
|
+
*/
|
|
58
|
+
export declare function isValidPackageName(name: string): boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Validate version string (basic check)
|
|
61
|
+
* @param version - Version string
|
|
62
|
+
* @returns true if valid
|
|
63
|
+
*/
|
|
64
|
+
export declare function isValidVersion(version: string): boolean;
|
|
65
|
+
/**
|
|
66
|
+
* Extract metadata from package metadata
|
|
67
|
+
* Filters and normalizes metadata fields
|
|
68
|
+
* @param metadata - Raw metadata object
|
|
69
|
+
* @returns Filtered metadata
|
|
70
|
+
*/
|
|
71
|
+
export declare function extractCoreMetadata(metadata: Record<string, any>): Record<string, any>;
|
|
72
|
+
/**
|
|
73
|
+
* Generate JSON API response for package list (PEP 691)
|
|
74
|
+
* @param packages - List of package names
|
|
75
|
+
* @returns JSON object
|
|
76
|
+
*/
|
|
77
|
+
export declare function generateJsonRootResponse(packages: string[]): any;
|
|
78
|
+
/**
|
|
79
|
+
* Generate JSON API response for package files (PEP 691)
|
|
80
|
+
* @param packageName - Package name (normalized)
|
|
81
|
+
* @param files - List of files
|
|
82
|
+
* @returns JSON object
|
|
83
|
+
*/
|
|
84
|
+
export declare function generateJsonPackageResponse(packageName: string, files: IPypiFile[]): any;
|