trm-core
Version:
TRM (Transport Request Manager) Core
571 lines (570 loc) • 24.5 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.RegistryV2 = exports.PUBLIC_RESERVED_KEYWORD = void 0;
const RegistryType_1 = require("./RegistryType");
const normalize_url_1 = __importDefault(require("@esm2cjs/normalize-url"));
const axios_1 = require("axios");
const trm_registry_types_1 = require("trm-registry-types");
const TrmArtifact_1 = require("../trmPackage/TrmArtifact");
const FormData = __importStar(require("form-data"));
const trm_commons_1 = require("trm-commons");
const crypto_1 = require("crypto");
const protocol_1 = require("../protocol");
const opener_1 = __importDefault(require("opener"));
const commons_1 = require("../commons");
const node_cache_1 = __importDefault(require("node-cache"));
const AXIOS_CTX = "RegistryV2";
exports.PUBLIC_RESERVED_KEYWORD = 'public';
class RegistryV2 {
constructor(endpoint, name = 'Unknown', _coreVersion) {
this.endpoint = endpoint;
this.name = name;
this._coreVersion = _coreVersion;
this._cache = new node_cache_1.default({ stdTTL: 60, useClones: false });
var envEndpoint = process.env.TRM_PUBLIC_REGISTRY_ENDPOINT;
trm_commons_1.Logger.log(`TRM_PUBLIC_REGISTRY_ENDPOINT Environment variable: ${envEndpoint}`, true);
if (!envEndpoint || envEndpoint.trim().toLowerCase() === exports.PUBLIC_RESERVED_KEYWORD) {
envEndpoint = 'https://www.trmregistry.com/registry';
}
if (endpoint.trim().toLowerCase() === exports.PUBLIC_RESERVED_KEYWORD) {
this._registryType = RegistryType_1.RegistryType.PUBLIC;
}
else {
this._registryType = RegistryType_1.RegistryType.PRIVATE;
}
if (this._registryType === RegistryType_1.RegistryType.PUBLIC) {
this.endpoint = envEndpoint;
this.name = exports.PUBLIC_RESERVED_KEYWORD;
}
else {
this.endpoint = endpoint;
}
trm_commons_1.Logger.log(`Endpoint type: ${this._registryType}`, true);
trm_commons_1.Logger.log(`Endpoint before normalize: ${this.endpoint}`, true);
this.endpoint = (0, normalize_url_1.default)(this.endpoint, {
stripHash: true,
removeQueryParameters: true
});
trm_commons_1.Logger.log(`Endpoint after normalize: ${this.endpoint}`, true);
if (this.endpoint.length > 100) {
throw new Error(`Registry address length is too long! Maximum allowed is 100.`);
}
this._axiosInstance = (0, commons_1.getAxiosInstance)({
baseURL: this.endpoint,
headers: this.getDefaultAxiosHeaders()
}, AXIOS_CTX);
}
getDefaultAxiosHeaders() {
var axiosHeaders = new axios_1.AxiosHeaders();
if (!this._userAgent) {
try {
this._userAgent = `trm-core v${this._coreVersion || (0, commons_1.getNodePackage)().version}`;
}
catch (_a) {
this._userAgent = `trm-core with unknown version`;
}
}
axiosHeaders.setUserAgent(this._userAgent);
return axiosHeaders;
}
compare(registry) {
if (registry instanceof RegistryV2) {
return this.endpoint === registry.endpoint;
}
else {
return false;
}
}
getRegistryType() {
return this._registryType;
}
authenticate() {
return __awaiter(this, arguments, void 0, function* (defaultData = {}) {
trm_commons_1.Logger.log(`Registry authentication request`, true);
const ping = yield this.ping();
trm_commons_1.Logger.log(`Registry authentication type is: ${ping.authentication_type}`, true);
if (ping.authentication_type !== trm_registry_types_1.AuthenticationType.NO_AUTH) {
if (ping.authentication_type === trm_registry_types_1.AuthenticationType.BASIC) {
yield this._basicAuth(defaultData);
}
if (ping.authentication_type === trm_registry_types_1.AuthenticationType.OAUTH2) {
yield this._oauth2(defaultData);
}
if (ping.authentication_type === trm_registry_types_1.AuthenticationType.TOKEN) {
yield this._tokenAuth(defaultData);
}
}
this._cache.flushAll();
return this;
});
}
_basicAuth() {
return __awaiter(this, arguments, void 0, function* (defaultData = {}) {
var axiosHeaders = this.getDefaultAxiosHeaders();
var axiosDefaults = {
baseURL: this.endpoint,
headers: axiosHeaders
};
var username = defaultData.username;
var password = defaultData.password;
const inq1 = yield trm_commons_1.Inquirer.prompt([{
type: "input",
name: "username",
message: "Registry username",
validate: (input) => {
return input ? true : false;
},
when: !username
}, {
type: "password",
name: "password",
message: "Registry password",
validate: (input) => {
return input ? true : false;
},
when: !password
}]);
username = username || inq1.username;
password = password || inq1.password;
const basicAuth = `${username}:${password}`;
const encodedBasicAuth = Buffer.from(basicAuth).toString('base64');
axiosHeaders.setAuthorization(`Basic ${encodedBasicAuth}`);
this._axiosInstance = (0, commons_1.getAxiosInstance)(axiosDefaults, AXIOS_CTX);
this._authData = {
username,
password
};
});
}
_tokenAuth() {
return __awaiter(this, arguments, void 0, function* (defaultData = {}) {
var axiosHeaders = this.getDefaultAxiosHeaders();
var axiosDefaults = {
baseURL: this.endpoint,
headers: axiosHeaders
};
var token = defaultData.token;
if (!token && this._registryType == RegistryType_1.RegistryType.PUBLIC) {
trm_commons_1.Logger.info(`To authenticate, generate a new token.`);
trm_commons_1.Logger.info(`Follow the instructions https://docs.trmregistry.com/#/registry/public/authentication.`);
}
const inq1 = yield trm_commons_1.Inquirer.prompt([{
type: "input",
name: "token",
message: "Registry token",
validate: (input) => {
return input ? true : false;
},
when: !token
}]);
token = token || inq1.token;
axiosHeaders.setAuthorization(`Bearer ${token}`);
this._axiosInstance = (0, commons_1.getAxiosInstance)(axiosDefaults, AXIOS_CTX);
this._authData = {
token
};
});
}
_oauth2() {
return __awaiter(this, arguments, void 0, function* (defaultData = {}) {
const ping = yield this.ping();
var runAuthFlow = false;
const accessToken = defaultData.access_token;
const refreshToken = defaultData.refresh_token;
const tokenExpiry = defaultData.expires_in;
const accessTokenTimestamp = defaultData.access_token_timestamp;
const currentDate = new Date();
var authData;
var oAuth2Request;
var oAuth2Response;
if (accessToken && accessTokenTimestamp && tokenExpiry) {
try {
const tokenDate = new Date(accessTokenTimestamp);
const elapsedSeconds = (currentDate.getTime() - tokenDate.getTime()) / 1000;
if (elapsedSeconds >= parseInt(tokenExpiry)) {
if (refreshToken) {
oAuth2Request = {
grant_type: "refresh_token",
refresh_token: refreshToken
};
oAuth2Response = (yield ((0, commons_1.getAxiosInstance)({
baseURL: this.endpoint
}, AXIOS_CTX)).post('/auth', oAuth2Request)).data;
runAuthFlow = false;
authData = {
access_token: oAuth2Response.access_token,
expires_in: oAuth2Response.expires_in,
refresh_token: refreshToken,
access_token_timestamp: currentDate.getTime()
};
}
else {
runAuthFlow = true;
}
}
else {
runAuthFlow = false;
authData = {
access_token: accessToken,
expires_in: tokenExpiry,
refresh_token: refreshToken,
access_token_timestamp: accessTokenTimestamp
};
}
}
catch (e) {
runAuthFlow = true;
}
}
else {
runAuthFlow = true;
}
if (runAuthFlow) {
const oAuth2 = ping.authentication_data;
const oAuth2ProtocolPath = "//oauth2";
const sRedirectUri = `trm:${oAuth2ProtocolPath}`;
const oAuth2Url = new URL(oAuth2.authorization_url);
const oAuth2State = (0, crypto_1.randomUUID)();
oAuth2Url.searchParams.append("client_id", oAuth2.client_id);
oAuth2Url.searchParams.append("response_type", oAuth2.response_type);
oAuth2Url.searchParams.append("redirect_uri", sRedirectUri);
oAuth2Url.searchParams.append("state", oAuth2State);
var sAuth2Url = oAuth2Url.toString();
if (oAuth2.scope) {
sAuth2Url = `${sAuth2Url}&scope=${oAuth2.scope}`;
}
trm_commons_1.Logger.info(`Open login url at ${sAuth2Url}`);
(0, opener_1.default)(sAuth2Url);
const oAuth2Callback = yield new protocol_1.Protocol().run();
if (oAuth2Callback.path.startsWith(sRedirectUri)) {
if (oAuth2Callback.parameters.state != oAuth2State) {
throw new Error("Different state received in callback.");
}
oAuth2Request = {
code: oAuth2Callback.parameters.code,
grant_type: "authorization_code",
redirect_uri: sRedirectUri
};
oAuth2Response = (yield ((0, commons_1.getAxiosInstance)({
baseURL: this.endpoint
}, AXIOS_CTX)).post('/auth', oAuth2Request)).data;
if (oAuth2Response.token_type !== "Bearer") {
throw new Error('Unknown token type.');
}
authData = {
access_token: oAuth2Response.access_token,
expires_in: oAuth2Response.expires_in,
refresh_token: oAuth2Response.refresh_token,
access_token_timestamp: currentDate.getTime()
};
}
else {
throw new Error("Callback received on a different uri.");
}
}
this._authData = authData;
var axiosHeaders = this.getDefaultAxiosHeaders();
var axiosDefaults = {
baseURL: this.endpoint,
headers: axiosHeaders
};
axiosHeaders.setAuthorization(`Bearer ${this._authData.access_token}`);
this._axiosInstance = (0, commons_1.getAxiosInstance)(axiosDefaults, AXIOS_CTX);
});
}
getAuthData() {
return this._authData;
}
ping() {
return __awaiter(this, void 0, void 0, function* () {
var data = this._cache.get('ping');
if (!data) {
try {
data = (yield this._axiosInstance.get('/', {
headers: {}
})).data;
}
catch (e) {
if (e.errors) {
e.errors.forEach(err => trm_commons_1.Logger.error(err.message));
}
data = new Error(`Registry "${this.name}" cannot be reached.`);
}
this._cache.set('ping', data);
}
if (data instanceof Error) {
throw data;
}
else {
return data;
}
});
}
whoAmI() {
return __awaiter(this, void 0, void 0, function* () {
var data = this._cache.get('whoami');
if (!data) {
try {
data = (yield this._axiosInstance.get('/whoami')).data;
}
catch (e) {
data = e;
}
this._cache.set('whoami', data);
}
if (data instanceof Error) {
throw data;
}
else {
return data;
}
});
}
getPackage(fullName_1) {
return __awaiter(this, arguments, void 0, function* (fullName, version = 'latest') {
var data = this._cache.get(`package-${fullName}-${version}`);
if (!data) {
var ttl;
try {
data = (yield this._axiosInstance.get(`/package/${fullName}`, {
params: {
version: encodeURIComponent(version)
}
})).data;
if (data.download_link_expiry) {
try {
ttl = Math.max(0, Math.floor((data.download_link_expiry - Date.now()) / 1000));
}
catch (_a) { }
}
}
catch (e) {
data = e;
}
this._cache.set(`package-${fullName}-${version}`, data, ttl);
}
if (data instanceof Error) {
throw data;
}
else {
return data;
}
});
}
downloadArtifact(fullName_1) {
return __awaiter(this, arguments, void 0, function* (fullName, version = 'latest') {
var _a;
const packageData = yield this.getPackage(fullName, version);
const chunks = [];
let buffer;
const logProgress = trm_commons_1.Logger.progressbar(`↓ ${fullName} ${version} [{bar}] {percentage}% | {value}/{total} bytes`, '>');
try {
const response = yield this._axiosInstance.get(packageData.download_link, {
headers: {
Accept: 'application/octet-stream',
},
maxRedirects: 10,
responseType: 'stream',
validateStatus: s => s >= 200 && s < 400,
});
const totalBytes = Number((_a = response.headers['content-length']) !== null && _a !== void 0 ? _a : 0);
let downloadedBytes = 0;
if (totalBytes > 0) {
logProgress.start(totalBytes, 0);
}
yield new Promise((resolve, reject) => {
response.data.on('data', (chunk) => {
chunks.push(chunk);
downloadedBytes += chunk.length;
if (totalBytes > 0) {
logProgress.update(downloadedBytes);
}
});
response.data.on('end', () => resolve());
response.data.on('error', reject);
});
if (totalBytes > 0) {
logProgress.stop();
}
buffer = Buffer.concat(chunks);
}
catch (e) {
try {
logProgress.stop();
}
catch (_b) {
}
trm_commons_1.Logger.error(e.toString(), true);
trm_commons_1.Logger.error(`Failed to fetch package at ${packageData.download_link}: ${e.message}`);
throw e;
}
return new TrmArtifact_1.TrmArtifact(buffer);
});
}
validatePublish(fullName_1) {
return __awaiter(this, arguments, void 0, function* (fullName, version = 'latest', isPrivate) {
const status = (yield this._axiosInstance.get(`/publish/check/${fullName}`, {
params: {
version: encodeURIComponent(version),
private: isPrivate ? 'X' : 'N'
}
})).status;
if (status !== 204) {
throw new Error(`Package cannot be published`);
}
});
}
publish(fullName, version, artifact, readme, tags) {
return __awaiter(this, void 0, void 0, function* () {
const fileName = `${fullName}_v${version}`.replace('.', '_') + '.trm';
const formData = new FormData.default();
formData.append('artifact', artifact.binary, {
filename: fileName,
contentType: 'application/octet-stream'
});
if (readme) {
formData.append('readme', Buffer.from(readme), {
filename: 'readme.md',
contentType: 'text/markdown'
});
}
var params = { version, tags };
if (!tags) {
delete params.tags;
}
yield this._axiosInstance.post(`/publish/${fullName}`, formData, {
params,
headers: formData.getHeaders()
});
});
}
unpublish(fullName, version) {
return __awaiter(this, void 0, void 0, function* () {
yield this._axiosInstance.post(`/unpublish/${fullName}`, null, {
params: {
version: encodeURIComponent(version)
}
});
});
}
deprecate(fullName, version, deprecate) {
return __awaiter(this, void 0, void 0, function* () {
yield this._axiosInstance.post(`/deprecate/${fullName}`, {
deprecate_note: deprecate.deprecate_note
}, {
params: {
version: encodeURIComponent(version)
}
});
});
}
addDistTag(fullName, distTag) {
return __awaiter(this, void 0, void 0, function* () {
const status = (yield this._axiosInstance.put(`/package/tag/${fullName}`, distTag)).status;
if (status !== 204) {
throw new Error(`Cannot add tag "${distTag.tag.trim().toUpperCase()}"`);
}
});
}
rmDistTag(fullName, distTag) {
return __awaiter(this, void 0, void 0, function* () {
const status = (yield this._axiosInstance.delete(`/package/tag/${fullName}`, {
data: distTag
})).status;
if (status !== 204) {
throw new Error(`Cannot remove tag "${distTag.tag.trim().toLowerCase()}"`);
}
});
}
contents(fullName_1) {
return __awaiter(this, arguments, void 0, function* (fullName, version = 'latest') {
var _a;
const chunks = [];
const logProgress = trm_commons_1.Logger.progressbar(`↓ ${fullName} ${version} contents [{bar}] {percentage}% | {value}/{total} bytes`, '>');
try {
const response = yield this._axiosInstance.get(`/package/contents/${fullName}`, {
params: {
version: encodeURIComponent(version),
},
responseType: 'stream',
});
const totalBytes = Number((_a = response.headers['content-length']) !== null && _a !== void 0 ? _a : 0);
let downloadedBytes = 0;
if (totalBytes > 0) {
logProgress.start(totalBytes, 0);
}
yield new Promise((resolve, reject) => {
response.data.on('data', (chunk) => {
chunks.push(chunk);
downloadedBytes += chunk.length;
if (totalBytes > 0) {
logProgress.update(downloadedBytes);
}
});
response.data.on('end', () => resolve());
response.data.on('error', reject);
});
if (totalBytes > 0) {
logProgress.stop();
}
const buffer = Buffer.concat(chunks);
return JSON.parse(buffer.toString('utf-8'));
}
catch (e) {
try {
logProgress.stop();
}
catch (_b) { }
trm_commons_1.Logger.error(e.toString(), true);
trm_commons_1.Logger.error(`Failed to fetch contents for ${fullName}: ${e.message}`, true);
throw e;
}
});
}
}
exports.RegistryV2 = RegistryV2;