Skip to content

Instantly share code, notes, and snippets.

@rosinghal
Last active September 11, 2020 13:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rosinghal/198d96d1f12254a9a26d14c68b74b413 to your computer and use it in GitHub Desktop.
Save rosinghal/198d96d1f12254a9a26d14c68b74b413 to your computer and use it in GitHub Desktop.
NodeJS script to clone or delete app from CloudWays
const Axios = require("axios");
const readline = require("readline");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
const axiosInstance = Axios.create({
baseURL: "https://api.cloudways.com/api/v1"
});
const getAccessToken = (email, api_key) => {
return axiosInstance.post("/oauth/access_token", {
email,
api_key
});
};
const listServers = () => {
return axiosInstance.get(`/server`);
};
const asyncForEach = async (array, callback) => {
for (let index = 0; index < array.length; index++) {
await callback(array[index], index, array);
}
};
const sleep = s => {
// console.log(`Sleeping for ${s} second(s)`);
return new Promise(resolve => setTimeout(resolve, s * 1000));
};
const cloneAppToOtherServer = (
server_id,
app_id,
destination_server_id,
app_label
) => {
return axiosInstance.post("/app/cloneToOtherServer", {
server_id,
app_id,
destination_server_id,
app_label
});
};
const updateApplicationLabel = (server_id, app_id, label) => {
return axiosInstance.put(`/app/${app_id}`, {
server_id,
label
});
};
const updateApplicationCname = (server_id, app_id, cname) => {
return axiosInstance.post("/app/manage/cname", {
server_id,
app_id,
cname
});
};
const installLetsencryptForApplication = (
server_id,
app_id,
ssl_email,
wild_card,
ssl_domains
) => {
return axiosInstance.post("/security/lets_encrypt_install", {
server_id,
app_id,
ssl_email,
wild_card,
ssl_domains
});
};
const enforceHTTPSForApplication = (server_id, app_id, status) => {
return axiosInstance.post("/app/manage/enforce_https", {
server_id,
app_id,
status
});
};
const resetPermissionsForApplication = (server_id, app_id, ownership) => {
return axiosInstance.post("/app/manage/reset_permissions", {
server_id,
app_id,
ownership
});
};
const updateSymlinkForApplication = (server_id, app_id, symlink) => {
return axiosInstance.post("/app/manage/symlink", {
server_id,
app_id,
symlink
});
};
const setToken = async (email, token) => {
const { access_token } = (await getAccessToken(email, token)).data;
axiosInstance.defaults.headers.common[
"Authorization"
] = `Bearer ${access_token}`;
};
const getOperationStatus = async operation_id => {
return axiosInstance.get(`/operation/${operation_id}`);
};
const deleteApp = async (server_id, app_id) => {
return axiosInstance.delete(`/app/${app_id}`, {
params: {
server_id
}
});
};
const cloneApps = async (source_server_id, destination_server_id) => {
console.log("Fetching apps list...");
const { servers } = (await listServers()).data;
const source_server = servers.find(s => s.id === source_server_id);
let op = null;
await asyncForEach(source_server.apps, async source_app => {
await new Promise(resolve => {
rl.question(
`Do you want to clone ${source_app.label} [y/N]? `,
async answer => {
if (["y"].includes(answer.toLowerCase())) {
try {
console.log(`Cloning ${source_app.label}...`);
// Cloning
const cloned_app = (
await cloneAppToOtherServer(
source_server_id,
source_app.id,
destination_server_id,
source_app.label
)
).data;
await onOperationComplete(
cloned_app.destination_op_id
);
console.log(`Cloned ${source_app.label}`);
// label
console.log(
`Updating label for ${source_app.label}...`
);
await updateApplicationLabel(
destination_server_id,
cloned_app.app_id,
source_app.label
);
console.log(
`Label updated for ${source_app.label}`
);
// CNAME
console.log(
`Updating CNAME for ${source_app.label}...`
);
op = await updateApplicationCname(
destination_server_id,
cloned_app.app_id,
source_app.cname
);
await onOperationComplete(op.data.operation_id);
console.log(
`CNAME updated for ${source_app.label}...`
);
// SSL
console.log(
`Installing Let's encrypt for ${source_app.label}...`
);
op = await installLetsencryptForApplication(
destination_server_id,
cloned_app.app_id,
source_app.lets_encrypt.ssl_email,
false,
source_app.lets_encrypt.ssl_domains
);
await onOperationComplete(op.data.operation_id);
console.log(
`Let's encrypt installed for ${source_app.label}`
);
// Force SSL
console.log(
`Enforcing HTTPS for ${source_app.label}...`
);
op = await enforceHTTPSForApplication(
destination_server_id,
cloned_app.app_id,
source_app.label.includes("demo")
? "disable"
: "enable"
);
await onOperationComplete(op.data.operation_id);
console.log(
`Enforced HTTPS for ${source_app.label}`
);
// Reset permissions
console.log(
`Resetting permissions for ${source_app.label}...`
);
op = await resetPermissionsForApplication(
destination_server_id,
cloned_app.app_id,
"master_user"
);
await onOperationComplete(op.data.operation_id);
console.log(
`Resetted permissions for ${source_app.label}`
);
// Symlink
console.log(
`Updating symlink for ${source_app.label}...`
);
await updateSymlinkForApplication(
destination_server_id,
cloned_app.app_id,
source_app.label.replace(/[^a-zA-Z0-9]/g, "_")
);
console.log(
`Symlink updated for ${source_app.label}`
);
console.log(`Finished for ${source_app.label}`);
} catch (error) {
console.error(
`Failed for ${source_app.label}`,
error.message,
error.response && error.response.data
? error.response.data
: ""
);
}
resolve();
} else {
console.log(`Skipping ${source_app.label}`);
resolve();
}
}
);
});
});
};
const onOperationComplete = async operation_id => {
console.log(`Operation in progress #${operation_id}...`);
try {
const op_response = (await getOperationStatus(operation_id)).data;
if (op_response.operation.is_completed === "1") {
console.log(`Operation completed #${operation_id}`);
return op_response;
} else {
await sleep(10);
return onOperationComplete(operation_id);
}
} catch (error) {
console.error(
error.message,
error.response &&
error.response.data &&
typeof error.response.data === "object"
? error.response.data
: ""
);
await sleep(10);
return onOperationComplete(operation_id);
}
};
const deleteApps = async source_server_id => {
const { servers } = (await listServers()).data;
const source_server = servers.find(s => s.id === source_server_id);
await asyncForEach(source_server.apps, async source_app => {
await new Promise(resolve => {
rl.question(
`Do you want to delete ${source_app.label} [y/N]? `,
async answer => {
if (["y"].includes(answer.toLowerCase())) {
console.log(`Deleting ${source_app.label}...`);
const delete_app_operation = (
await deleteApp(source_server_id, source_app.id)
).data;
await onOperationComplete(
delete_app_operation.operation_id
);
resolve();
} else {
console.log(`Skipping ${source_app.label}`);
resolve();
}
}
);
});
});
};
const init = async () => {
const cloudwaysAPIKey = await new Promise(resolve => {
rl.question(`Enter CloudWays API key: `, async res => {
resolve(res);
});
});
const cloudwaysEmail = await new Promise(resolve => {
rl.question(`Enter CloudWays email: `, async res => {
resolve(res);
});
});
const action = await new Promise(resolve => {
rl.question(
`Enter action that you want to perform [delete|clone]: `,
async res => {
resolve(res);
}
);
});
setToken(cloudwaysEmail, cloudwaysAPIKey);
switch (action) {
case "delete":
const serverId = await new Promise(resolve => {
rl.question(`Enter server ID: `, async res => {
resolve(res);
});
});
await deleteApps(serverId);
break;
case "clone":
const sourceServerId = await new Promise(resolve => {
rl.question(`Enter source server ID: `, async res => {
resolve(res);
});
});
const destinationServerId = await new Promise(resolve => {
rl.question(`Enter destination server ID: `, async res => {
resolve(res);
});
});
await cloneApps(sourceServerId, destinationServerId);
break;
default:
break;
}
rl.close();
};
init();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment