diff --git a/package.json b/package.json index e049e7c..9d97f65 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "scripts": { "formatter": "prettier --check './public/**/*.json'", "formatter-write": "prettier --write './public/**/*.json'", - "build": "rm -rf ./dist/ && mkdir -p dist && node ./scripts/build_one_click_apps.js", + "build": "rm -rf ./dist/ && mkdir -p dist && node ./scripts/build_one_click_apps.js && node ./scripts/build_one_click_apps_from_v4.js", "validate_json": "node ./scripts/validate_json.js", "publish": "npm run build && ./scripts/publish-from-actions.sh" }, diff --git a/public/v4/apps/privatebin.yml b/public/v4/apps/privatebin.yml new file mode 100644 index 0000000..5dd18af --- /dev/null +++ b/public/v4/apps/privatebin.yml @@ -0,0 +1,35 @@ +captainVersion: 4 +services: + "$$cap_appname": + image: privatebin/nginx-fpm-alpine:$$cap_version + environment: + TZ: "$$cap_tz" + PHP_TZ: "$$cap_tz" + volumes: + - "$$cap_appname-data:/srv/data" + caproverExtra: + containerHttpPort: '8080' +caproverOneClickApp: + variables: + - id: "$$cap_version" + label: PrivateBin Version + defaultValue: '1.3.4' + description: Check out their Docker page for the valid tags https://hub.docker.com/r/privatebin/nginx-fpm-alpine/tags + validRegex: "/^([^\\s^\\/])+$/" + - id: "$$cap_tz" + label: Time Zone + defaultValue: America/New_York + description: Get yours from https://en.wikipedia.org/wiki/List_of_tz_database_time_zones + validRegex: "/.{1,}/" + instructions: + start: |- + PrivateBin is a minimalist, open source online pastebin where the server has zero knowledge of pasted data. + Data is encrypted and decrypted in the browser using 256bit AES in Galois Counter mode. + + More details: https://github.com/PrivateBin/PrivateBin + end: |- + PrivateBin has been successfully deployed! + displayName: PrivateBin + description: A minimalist, open source online pastebin where the server has zero knowledge of pasted data. Data is encrypted/decrypted in the browser using 256 bits AES. + documentation: See https://github.com/PrivateBin/docker-nginx-fpm-alpine + \ No newline at end of file diff --git a/public/v4/logos/privatebin.png b/public/v4/logos/privatebin.png new file mode 100644 index 0000000..48e6608 Binary files /dev/null and b/public/v4/logos/privatebin.png differ diff --git a/scripts/build_one_click_apps.js b/scripts/build_one_click_apps.js index b08f04d..69fa232 100644 --- a/scripts/build_one_click_apps.js +++ b/scripts/build_one_click_apps.js @@ -77,11 +77,11 @@ function convertV2toV4(v2String) { parsed[propertyName] = undefined; } - parsed.captainVersion = 4; - parsed.caproverOneClickApp = {}; - parsed.services = parsed.dockerCompose.services; parsed.dockerCompose = undefined; + + parsed.captainVersion = 4; + parsed.caproverOneClickApp = {}; moveProperty('variables'); moveProperty('instructions'); diff --git a/scripts/build_one_click_apps_from_v4.js b/scripts/build_one_click_apps_from_v4.js new file mode 100644 index 0000000..20bcda3 --- /dev/null +++ b/scripts/build_one_click_apps_from_v4.js @@ -0,0 +1,170 @@ +/*jshint esversion: 6 */ +const path = require('path'); +const yaml = require('yaml'); +const fs = require('fs-extra'); + +const pathOfPublic = path.join(__dirname, '..', `public`); + +const pathOfDist = path.join(__dirname, '..', `dist`); + +const pathOfDistV2 = path.join(pathOfDist, 'v2'); +const pathOfDistV3 = path.join(pathOfDist, 'v3'); +const pathOfDistV4 = path.join(pathOfDist, 'v4'); + +const pathOfSourceDirectory = path.join(pathOfPublic, 'v4'); +const pathOfSourceDirectoryApps = path.join(pathOfSourceDirectory, 'apps'); +const pathOfSourceDirectoryLogos = path.join(pathOfSourceDirectory, 'logos'); + +/** + * Creates a listing of apps for GET http://oneclickapps.caprover.com/v4 + * { + "oneClickApps": [ + { + "name": "adminer", + "displayName": "Adminer", + "description": "Adminer (formerly phpMinAdmin) is a full-featured database management tool written in PHP", + "isOfficial": true, + "logoUrl": "adminer.png" + },.....]} + */ +function createAppList(appsFileNames, pathOfApps) { + const apps = appsFileNames.filter(v => `${v}`.endsWith('.yml')); + + if (apps.length !== appsFileNames.length) { + throw new Error('All files in v4 must end with .yml extension!'); + } + + const appDetails = []; + + for (var i = 0; i < apps.length; i++) { + const contentString = fs.readFileSync(path.join(pathOfApps, apps[i]), 'utf-8'); + const content = yaml.parse(contentString); + const captainVersion = `${content.captainVersion}`; + + apps[i] = apps[i].replace('.yml', ''); + const caproverOneClickApp = content.caproverOneClickApp; + + if (captainVersion === '4') { + if (!caproverOneClickApp.displayName) { + caproverOneClickApp.displayName = apps[i]; + caproverOneClickApp.displayName = caproverOneClickApp.displayName.substr(0, 1).toUpperCase() + + caproverOneClickApp.displayName.substring(1, caproverOneClickApp.displayName.length); + } + if (!caproverOneClickApp.description) caproverOneClickApp.description = ''; + + appDetails[i] = { + name: apps[i], + displayName: caproverOneClickApp.displayName, + description: caproverOneClickApp.description, + isOfficial: `${caproverOneClickApp.isOfficial}`.toLowerCase().trim() === 'true', + logoUrl: apps[i] + '.png' + }; + } else { + throw new Error('Unknown captain-version: ' + captainVersion); + } + + } + + return { + appList: apps, + appDetails: appDetails + }; +} + +function convertV4toV2(v4String) { + const parsed = JSON.parse(v4String); + if (`${parsed.captainVersion}` !== '4') { + throw new Error('CaptainVersion must be 4 for this conversion'); + } + + function moveProperty(propertyName) { + parsed[propertyName] = parsed.caproverOneClickApp[propertyName]; + } + + parsed.dockerCompose = { + services: parsed.services + }; + parsed.services = undefined; + + parsed.captainVersion = 2; + + + moveProperty('variables'); + moveProperty('instructions'); + moveProperty('displayName'); + moveProperty('description'); + moveProperty('documentation'); + + Object.keys(parsed.dockerCompose.services).forEach(serviceName => { + const service = parsed.dockerCompose.services[serviceName]; + + if (!service.caproverExtra) { + return; + } + + if (service.caproverExtra.containerHttpPort) { + service.containerHttpPort = service.caproverExtra.containerHttpPort; + } + if (service.caproverExtra.dockerfileLines) { + service.dockerfileLines = service.caproverExtra.dockerfileLines; + } + if (service.caproverExtra.notExposeAsWebApp) { + service.notExposeAsWebApp = service.caproverExtra.notExposeAsWebApp; + } + + service.caproverExtra = undefined; + }); + + parsed.caproverOneClickApp = undefined; + return parsed; +} + + +function buildDist() { + return fs.readdir(pathOfSourceDirectoryApps) + .then(function (appsFileNames) { // [ app1.yml app2.yml .... ] + + appsFileNames.forEach(appFileName => { + + console.log('Building dist for ' + appFileName); + + const pathOfAppFileInSource = path.join(pathOfSourceDirectoryApps, appFileName); + const contentParsed = yaml.parse(fs.readFileSync(pathOfAppFileInSource, 'utf-8')); + + //v4 + fs.outputJsonSync(path.join(pathOfDistV4, `apps`, appFileName.split('.')[0]), contentParsed); + + //v3 + fs.outputJsonSync(path.join(pathOfDistV3, `apps`, appFileName.split('.')[0]), convertV4toV2(JSON.stringify(contentParsed))); + + //v2 + fs.outputJsonSync(path.join(pathOfDistV2, `apps`, appFileName.split('.')[0] + '.json'), convertV4toV2(JSON.stringify(contentParsed))); + }); + + fs.copySync(pathOfSourceDirectoryLogos, path.join(pathOfDistV2, `logos`)); + fs.copySync(pathOfSourceDirectoryLogos, path.join(pathOfDistV3, `logos`)); + fs.copySync(pathOfSourceDirectoryLogos, path.join(pathOfDistV4, `logos`)); + + const allAppsList = createAppList(appsFileNames, pathOfSourceDirectoryApps); + const v3List = { + oneClickApps: allAppsList.appDetails + }; + fs.outputJsonSync(path.join(pathOfDistV2, 'autoGeneratedList.json'), allAppsList); + fs.outputJsonSync(path.join(pathOfDistV2, 'list'), v3List); + fs.outputJsonSync(path.join(pathOfDistV3, 'list'), v3List); + fs.outputJsonSync(path.join(pathOfDistV4, 'list'), v3List); + }) + .then(function () { + return fs.copySync(path.join(pathOfPublic, 'CNAME'), path.join(pathOfDist, 'CNAME')); + }); +} + + +Promise.resolve() + .then(function () { + return buildDist(); + }) + .catch(function (err) { + console.error(err); + process.exit(127); + }); \ No newline at end of file