api refactoring

This commit is contained in:
Brian Seymour 2021-03-12 23:01:04 -06:00
parent 5a05537a7f
commit b3d18600cd
11 changed files with 358 additions and 327 deletions

View file

@ -11,66 +11,73 @@ const crypto = require('crypto');
const runtime = require('../runtime');
class Package {
constructor({language, version, download, checksum}){
constructor({ language, version, download, checksum }){
this.language = language;
this.version = semver.parse(version);
this.checksum = checksum;
this.download = download;
}
get installed(){
get installed() {
return fss.exists_sync(path.join(this.install_path, globals.pkg_installed_file));
}
get download_url(){
return this.download;
}
get install_path(){
return path.join(config.data_directory,
get install_path() {
return path.join(
config.data_directory,
globals.data_directories.packages,
this.language,
this.version.raw);
this.version.raw
);
}
async install(){
if(this.installed) throw new Error('Already installed');
async install() {
if (this.installed) {
throw new Error('Already installed');
}
logger.info(`Installing ${this.language}-${this.version.raw}`);
if(fss.exists_sync(this.install_path)){
if (fss.exists_sync(this.install_path)) {
logger.warn(`${this.language}-${this.version.raw} has residual files. Removing them.`);
await fs.rm(this.install_path, {recursive: true, force: true});
await fs.rm(this.install_path, { recursive: true, force: true });
}
logger.debug(`Making directory ${this.install_path}`);
await fs.mkdir(this.install_path, {recursive: true});
logger.debug(`Downloading package from ${this.download} in to ${this.install_path}`);
const pkgpath = path.join(this.install_path, 'pkg.tar.gz');
const download = await fetch(this.download);
logger.debug(`Downloading package from ${this.download_url} in to ${this.install_path}`);
const pkgpath = path.join(this.install_path, "pkg.tar.gz");
const download = await fetch(this.download_url);
const file_stream = fss.create_write_stream(pkgpath);
await new Promise((resolve, reject) => {
download.body.pipe(file_stream)
download.body.on("error", reject)
file_stream.on("finish", resolve)
download.body.pipe(file_stream);
download.body.on('error', reject);
file_stream.on('finish', resolve);
});
logger.debug('Validating checksums');
logger.debug(`Assert sha256(pkg.tar.gz) == ${this.checksum}`)
logger.debug(`Assert sha256(pkg.tar.gz) == ${this.checksum}`);
const cs = crypto.create_hash("sha256")
.update(fss.readFileSync(pkgpath))
.digest('hex');
if(cs != this.checksum) throw new Error(`Checksum miss-match want: ${val} got: ${cs}`);
if (cs !== this.checksum) {
throw new Error(`Checksum miss-match want: ${val} got: ${cs}`);
}
logger.debug(`Extracting package files from archive ${pkgpath} in to ${this.install_path}`);
await new Promise((resolve, reject)=>{
await new Promise((resolve, reject) => {
const proc = cp.exec(`bash -c 'cd "${this.install_path}" && tar xzf ${pkgpath}'`);
proc.once('exit', (code,_)=>{
if(code == 0) resolve();
else reject(new Error('Failed to extract package'));
proc.once('exit', (code, _) => {
code === 0 ? resolve() : reject();
});
proc.stdout.pipe(process.stdout);
proc.stderr.pipe(process.stderr);
@ -80,28 +87,35 @@ class Package {
logger.debug('Registering runtime');
new runtime.Runtime(this.install_path);
logger.debug('Caching environment');
const get_env_command = `cd ${this.install_path}; source environment; env`;
const envout = await new Promise((resolve, reject)=>{
var stdout = '';
const proc = cp.spawn('env',['-i','bash','-c',`${get_env_command}`], {
stdio: ['ignore', 'pipe', 'pipe']});
proc.once('exit', (code,_)=>{
if(code == 0) resolve(stdout);
else reject(new Error('Failed to cache environment'));
const envout = await new Promise((resolve, reject) => {
let stdout = '';
const proc = cp
.spawn(
'env',
['-i','bash','-c',`${get_env_command}`],
{
stdio: ['ignore', 'pipe', 'pipe']
}
);
proc.once('exit', (code, _) => {
code === 0 ? resolve() : reject();
});
proc.stdout.on('data', (data)=>{
proc.stdout.on('data', data => {
stdout += data;
});
proc.once('error', reject);
});
const filtered_env = envout.split('\n')
.filter(l=>!['PWD','OLDPWD','_', 'SHLVL'].includes(l.split('=',2)[0]))
const filtered_env = envout
.split('\n')
.filter(l => !['PWD','OLDPWD','_', 'SHLVL'].includes(l.split('=',2)[0]))
.join('\n');
await fs.write_file(path.join(this.install_path, '.env'), filtered_env);
@ -116,7 +130,9 @@ class Package {
version: this.version.raw
};
}
}
module.exports = {Package};
module.exports = {
Package
};

View file

@ -4,66 +4,98 @@ const fetch = require('node-fetch');
const config = require('../config');
const { Package } = require('./package');
const get_package_list = async () => {
const repo_content = await fetch(config.repo_url).then(x => x.text());
async function get_package_list(){
const repo_content = await fetch(config.repo_url).then(x=>x.text());
const entries = repo_content.split('\n').filter(x=>x.length > 0);
const entries = repo_content
.split('\n')
.filter(x => x.length > 0);
return entries.map(line => {
const [language, version, checksum, download] = line.split(',',4);
return new Package({language, version, checksum, download});
})
}
const [ language, version, checksum, download ] = line.split(',', 4);
return new Package({
language,
version,
checksum,
download
});
});
};
async function get_package(lang, version){
const get_package async (lang, version) => {
const packages = await get_package_list();
const candidates = packages.filter(
pkg => pkg.language == lang && semver.satisfies(pkg.version, version)
);
return candidates.sort((a,b)=>semver.rcompare(a.version,b.version))[0] || null;
}
const candidates = packages
.filter(pkg => {
return pkg.language == lang && semver.satisfies(pkg.version, version)
});
candidates.sort((a, b) => semver.rcompare(a.version, b.version));
return candidates[0] || null;
};
module.exports = {
async package_list(req, res){
// GET /packages
// GET /packages
async package_list(req, res) {
logger.debug('Request to list packages');
const packages = await get_package_list();
res.json_success({
packages: packages.map(pkg=>({
language: pkg.language,
language_version: pkg.version.raw,
installed: pkg.installed
}))
});
packages = packages
.map(pkg => {
return {
language: pkg.language,
language_version: pkg.version.raw,
installed: pkg.installed
};
});
return res
.status(200)
.send(packages);
},
async package_install(req,res){
// POST /packages/:language/:version
// POST /packages/:language/:version
async package_install(req, res) {
logger.debug('Request to install package');
const pkg = await get_package(req.params.language, req.params.version);
if(pkg == null) return res.json_error(`Requested package ${req.params.language}-${req.params.version} does not exist`, 404);
try{
const response = await pkg.install();
return res.json_success(response);
}catch(err){
logger.error(`Error while installing package ${pkg.language}-${pkg.version}:`, err.message);
res.json_error(err.message,500);
if (pkg == null) {
return res
.status(404)
.send({
message: `Requested package ${req.params.language}-${req.params.version} does not exist`
});
}
try {
const response = await pkg.install();
return res
.status(200)
.send(response);
} catch(e) {
logger.error(`Error while installing package ${pkg.language}-${pkg.version}:`, e.message);
return res
.status(500)
.send({
message: e.message
});
}
},
async package_uninstall(req,res){
// DELETE /packages/:language/:version
//res.json(req.body); //TODO
res.json_error('not implemented', 500);
// DELETE /packages/:language/:version
async package_uninstall(req, res) {
return res
.status(500)
.send({
message: 'Not implemented'
});
}
};
};