Compare commits

..

9 Commits

Author SHA1 Message Date
1f5352d9f5 3.0.3 2019-07-18 17:12:04 +02:00
f652cc72fe fix(core): update 2019-07-18 17:12:03 +02:00
f510408fce 3.0.2 2019-07-18 15:31:25 +02:00
e250e9b1a2 fix(core): update 2019-07-18 15:31:24 +02:00
5a9b7bbeee 3.0.1 2019-07-18 14:44:45 +02:00
1158b4ff99 fix(core): update 2019-07-18 14:44:45 +02:00
8eb777dd45 3.0.0 2019-07-18 14:25:40 +02:00
acf5c242d3 2.0.2 2019-07-18 14:25:10 +02:00
9e9dd8d935 fix(core): update 2019-07-18 14:25:10 +02:00
11 changed files with 222 additions and 60 deletions

19
package-lock.json generated
View File

@ -1,6 +1,6 @@
{ {
"name": "@mojoio/cloudflare", "name": "@mojoio/cloudflare",
"version": "2.0.1", "version": "3.0.3",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
@ -49,15 +49,14 @@
} }
}, },
"@gitzone/tsrun": { "@gitzone/tsrun": {
"version": "1.2.6", "version": "1.2.8",
"resolved": "https://verdaccio.lossless.one/@gitzone%2ftsrun/-/tsrun-1.2.6.tgz", "resolved": "https://verdaccio.lossless.one/@gitzone%2ftsrun/-/tsrun-1.2.8.tgz",
"integrity": "sha512-MTgQ7OiKfMpRQRumJp8aGuLCNNyWGjZSxsF+GfZ7cmg2DUtJRyCL0Ybkai1sPdMWG/zE8RUo50WvBKEChdVyvA==", "integrity": "sha512-G65DNcjegxF6w/O9MYD2mlHh+QVUiLuZvgLD5eamOOuWLhzuiE1tDzv5praJ/woJQzzZinDs+IyRyZ5rWKp/fQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"@gitzone/tsbuild": "^2.1.8", "@pushrocks/smartfile": "^7.0.4",
"@pushrocks/smartfile": "^7.0.2", "ts-node": "^8.3.0",
"ts-node": "^8.0.3", "typescript": "^3.5.3"
"typescript": "^3.4.2"
} }
}, },
"@gitzone/tstest": { "@gitzone/tstest": {
@ -568,7 +567,7 @@
}, },
"buffer-from": { "buffer-from": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", "resolved": "https://verdaccio.lossless.one/buffer-from/-/buffer-from-1.1.1.tgz",
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
"dev": true "dev": true
}, },
@ -1527,7 +1526,7 @@
}, },
"source-map": { "source-map": {
"version": "0.6.1", "version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "resolved": "https://verdaccio.lossless.one/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true "dev": true
}, },

View File

@ -1,6 +1,6 @@
{ {
"name": "@mojoio/cloudflare", "name": "@mojoio/cloudflare",
"version": "2.0.1", "version": "3.0.3",
"private": false, "private": false,
"description": "easy cloudflare management", "description": "easy cloudflare management",
"main": "dist/index.js", "main": "dist/index.js",
@ -33,7 +33,7 @@
}, },
"devDependencies": { "devDependencies": {
"@gitzone/tsbuild": "^2.1.11", "@gitzone/tsbuild": "^2.1.11",
"@gitzone/tsrun": "^1.2.6", "@gitzone/tsrun": "^1.2.8",
"@gitzone/tstest": "^1.0.24", "@gitzone/tstest": "^1.0.24",
"@pushrocks/qenv": "^4.0.0", "@pushrocks/qenv": "^4.0.0",
"@pushrocks/tapbundle": "^3.0.11", "@pushrocks/tapbundle": "^3.0.11",

View File

@ -1,15 +1,23 @@
// tslint:disable-next-line: no-implicit-dependencies
import { expect, tap } from '@pushrocks/tapbundle'; import { expect, tap } from '@pushrocks/tapbundle';
import cloudflare = require('../ts/index'); // tslint:disable-next-line: no-implicit-dependencies
import { Qenv } from '@pushrocks/qenv'; import { Qenv } from '@pushrocks/qenv';
import cloudflare = require('../ts/index');
const testQenv = new Qenv(process.cwd(), process.cwd() + '/.nogit'); const testQenv = new Qenv(process.cwd(), process.cwd() + '/.nogit');
const testCloudflareAccount = new cloudflare.CloudflareAccount(); const randomPrefix = Math.floor(Math.random() * 2000);
testCloudflareAccount.auth({ let testCloudflareAccount: cloudflare.CloudflareAccount;
tap.test('should create a valid instance of CloudflareAccount', async () => {
testCloudflareAccount = new cloudflare.CloudflareAccount({
email: testQenv.getEnvVarOnDemand('CF_EMAIL'), email: testQenv.getEnvVarOnDemand('CF_EMAIL'),
key: testQenv.getEnvVarOnDemand('CF_KEY') key: testQenv.getEnvVarOnDemand('CF_KEY')
}); });
});
const randomPrefix = Math.floor(Math.random() * 2000);
tap.skip.test('.listZones() -> should display an entire account', async tools => { tap.skip.test('.listZones() -> should display an entire account', async tools => {
tools.timeout(600000); tools.timeout(600000);
@ -60,4 +68,21 @@ tap.test('.purge(some.domain) -> should purge everything', async () => {
await testCloudflareAccount.purgeZone('bleu.de'); await testCloudflareAccount.purgeZone('bleu.de');
}); });
// WORKERS
tap.test('should create a worker', async () => {
const worker = await testCloudflareAccount.workerManager.createWorker('myawesomescript', `addEventListener('fetch', event => { event.respondWith(fetch(event.request)) })`);
await worker.setRoutes([
{
zoneName: 'bleu.de',
pattern: 'https://*bleu.de/hello'
}
]);
console.log(worker);
});
tap.test('should get workers', async () => {
const workerArray = await testCloudflareAccount.workerManager.listWorkers();
console.log(workerArray);
});
tap.start(); tap.start();

View File

@ -1,5 +1,5 @@
import plugins = require('./cloudflare.plugins'); import plugins = require('./cloudflare.plugins');
import * as interfaces from './interfaces/cloudflare.interfaces'; import * as interfaces from './interfaces';
// interfaces // interfaces
import { TDnsRecord } from '@tsclass/tsclass'; import { TDnsRecord } from '@tsclass/tsclass';
@ -9,19 +9,30 @@ import { ZoneManager } from './cloudflare.classes.zonemanager';
export class CloudflareAccount { export class CloudflareAccount {
private authEmail: string; private authEmail: string;
private authKey: string; private authKey: string;
private accountIdentifier: string;
public workerManager = new WorkerManager(this); public workerManager = new WorkerManager(this);
public zoneManager = new ZoneManager(this); public zoneManager = new ZoneManager(this);
constructor() { /**
// Nothing here * constructor sets auth information on the CloudflareAccountInstance
} * @param optionsArg
*/
public auth(optionsArg: { email: string; key: string }) { constructor(optionsArg: { email: string; key: string }) {
this.authEmail = optionsArg.email; this.authEmail = optionsArg.email;
this.authKey = optionsArg.key; this.authKey = optionsArg.key;
} }
public async getAccountIdentifier() {
if (!this.accountIdentifier) {
const route = `/accounts?page=1&per_page=20&direction=desc`;
const response: any = await this.request('GET', route);
this.accountIdentifier = response.result[0].id;
// console.log('Account identifier is: ' + this.accountIdentifier);
}
return this.accountIdentifier;
}
/** /**
* gets a zone id of a domain from cloudflare * gets a zone id of a domain from cloudflare
* @param domainName * @param domainName
@ -80,24 +91,30 @@ export class CloudflareAccount {
return response; return response;
} }
public removeRecord(domainNameArg: string, typeArg: TDnsRecord) { /**
const done = plugins.smartpromise.defer(); * removes a record from Cloudflare
* @param domainNameArg
* @param typeArg
*/
public async removeRecord(domainNameArg: string, typeArg: TDnsRecord): Promise<any> {
const domain = new plugins.smartstring.Domain(domainNameArg); const domain = new plugins.smartstring.Domain(domainNameArg);
this.getRecord(domain.fullName, typeArg).then(responseArg => { const cflareRecord = await this.getRecord(domain.fullName, typeArg);
if (responseArg) { if (cflareRecord) {
const requestRoute: string = const requestRoute: string = `/zones/${cflareRecord.zone_id}/dns_records/${cflareRecord.id}`;
'/zones/' + responseArg.zone_id + '/dns_records/' + responseArg.id; return await this.request('DELETE', requestRoute);
this.request('DELETE', requestRoute).then(responseArg => {
done.resolve(responseArg);
});
} else { } else {
done.reject(); throw new Error(`could not remove record for ${domainNameArg} with type ${typeArg}`);
} }
});
return done.promise;
} }
/**
* updates a record
* @param domainNameArg
* @param typeArg
* @param valueArg
*/
public updateRecord(domainNameArg: string, typeArg: string, valueArg) { public updateRecord(domainNameArg: string, typeArg: string, valueArg) {
// TODO: implement
const done = plugins.smartpromise.defer(); const done = plugins.smartpromise.defer();
const domain = new plugins.smartstring.Domain(domainNameArg); const domain = new plugins.smartstring.Domain(domainNameArg);
return done.promise; return done.promise;
@ -146,7 +163,12 @@ export class CloudflareAccount {
const respone = await this.request('DELETE', requestUrl, payload); const respone = await this.request('DELETE', requestUrl, payload);
} }
public request(methodArg: string, routeArg: string, dataArg = {}) { public request(
methodArg: string,
routeArg: string,
dataArg: any = {},
requestHeadersArg = {}
): Promise<any> {
const done = plugins.smartpromise.defer(); const done = plugins.smartpromise.defer();
const options: plugins.smartrequest.ISmartRequestOptions = { const options: plugins.smartrequest.ISmartRequestOptions = {
method: methodArg, method: methodArg,
@ -154,11 +176,14 @@ export class CloudflareAccount {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'X-Auth-Email': this.authEmail, 'X-Auth-Email': this.authEmail,
'X-Auth-Key': this.authKey, 'X-Auth-Key': this.authKey,
'Content-Length': Buffer.byteLength(JSON.stringify(dataArg)) 'Content-Length': Buffer.byteLength(JSON.stringify(dataArg)),
...requestHeadersArg
}, },
requestBody: dataArg requestBody: dataArg
}; };
// console.log(options);
let retryCount = 0; // count the amount of retries let retryCount = 0; // count the amount of retries
const makeRequest = async () => { const makeRequest = async () => {
@ -173,7 +198,7 @@ export class CloudflareAccount {
retryRequest(); retryRequest();
} else if (response.statusCode === 400) { } else if (response.statusCode === 400) {
console.log(`bad request for route ${routeArg}! Going to retry!`); console.log(`bad request for route ${routeArg}! Going to retry!`);
retryRequest(); console.log(response.body);
} else { } else {
console.log(response.statusCode); console.log(response.statusCode);
done.reject(new Error('request failed')); done.reject(new Error('request failed'));

View File

@ -0,0 +1,87 @@
import * as plugins from './cloudflare.plugins';
import * as interfaces from './interfaces';
import { WorkerManager } from './cloudflare.classes.workermanager';
export interface IWorkerRoute extends interfaces.ICflareWorkerRoute {
zoneName: string;
}
export class Worker {
// STATIC
public static async fromApiObject(workerManager: WorkerManager, apiObject): Promise<Worker> {
const newWorker = new Worker(workerManager);
Object.assign(newWorker, apiObject.result);
await newWorker.getRoutes();
return newWorker;
}
// INSTANCE
private workerManager: WorkerManager;
public script: string;
public id: string;
public etag: string;
// tslint:disable-next-line: variable-name
public created_on: string;
// tslint:disable-next-line: variable-name
public modified_on: string;
public routes: IWorkerRoute[] = [];
constructor(workerManagerArg: WorkerManager) {
this.workerManager = workerManagerArg;
}
/**
* gets all routes for a worker
*/
public async getRoutes() {
const zones = await this.workerManager.cfAccount.listZones();
for (const zone of zones) {
const requestRoute = `/zones/${zone.id}/workers/routes`;
const response: {result: interfaces.ICflareWorkerRoute[]} = await this.workerManager.cfAccount.request('GET', requestRoute);
for (const route of response.result) {
console.log('hey');
console.log(route);
console.log(this.id);
if (route.script === this.id) {
this.routes.push({...route, zoneName: zone.name});
}
}
}
}
public async setRoutes(routeArray: Array<{zoneName: string, pattern: string}>) {
for (const newRoute of routeArray) {
// lets determine wether a route is new, needs an update or already up to date.
let routeStatus: 'new' | 'needsUpdate' | 'alreadyUpToDate' = 'new';
let routeIdForUpdate: string;
for (const existingRoute of this.routes) {
if (existingRoute.pattern === newRoute.pattern) {
routeStatus = 'needsUpdate';
routeIdForUpdate = existingRoute.id;
if (existingRoute.script === this.id) {
routeStatus = 'alreadyUpToDate';
plugins.smartlog.defaultLogger.log('info', `route already exists, no update needed`);
}
}
}
// lets care about actually setting routes
if (routeStatus === 'new') {
const zoneId = await this.workerManager.cfAccount.getZoneId(newRoute.zoneName);
const requestRoute = `/zones/${zoneId}/workers/routes`;
await this.workerManager.cfAccount.request('POST', requestRoute, {
pattern: newRoute.pattern,
script: this.id
});
} else if (routeStatus === 'needsUpdate') {
const zoneId = await this.workerManager.cfAccount.getZoneId(newRoute.zoneName);
const requestRoute = `/zones/${zoneId}/workers/routes/${routeIdForUpdate}`;
await this.workerManager.cfAccount.request('PUT', requestRoute, {
pattern: newRoute.pattern,
script: this.id
});
}
}
}
}

View File

@ -1,5 +1,6 @@
import * as plugins from './cloudflare.plugins'; import * as plugins from './cloudflare.plugins';
import { CloudflareAccount } from './cloudflare.classes.account'; import { CloudflareAccount } from './cloudflare.classes.account';
import { Worker } from './cloudflare.classes.worker';
export class WorkerManager { export class WorkerManager {
public cfAccount: CloudflareAccount; public cfAccount: CloudflareAccount;
@ -7,4 +8,24 @@ export class WorkerManager {
constructor(cfAccountArg: CloudflareAccount) { constructor(cfAccountArg: CloudflareAccount) {
this.cfAccount = cfAccountArg; this.cfAccount = cfAccountArg;
} }
public async createWorker(workerName: string, workerScript: string): Promise<Worker> {
const accountIdentifier = await this.cfAccount.getAccountIdentifier();
const route = `/accounts/${accountIdentifier}/workers/scripts/${workerName}`;
const responseBody = await this.cfAccount.request('PUT', route, workerScript, {
'Content-Type': 'application/javascript',
'Content-Length': Buffer.byteLength(workerScript)
});
return Worker.fromApiObject(this, responseBody);
}
/**
* lists workers
*/
public async listWorkers() {
const accountIdentifier = await this.cfAccount.getAccountIdentifier();
const route = `/accounts/${accountIdentifier}/workers/scripts`;
const response = await this.cfAccount.request('GET', route);
console.log(response);
}
} }

View File

@ -0,0 +1,15 @@
export interface ICflareRecord {
id: string;
type: string;
name: string;
content: string;
proxiable: boolean;
proxied: boolean;
ttl: number;
locked: boolean;
zone_id: string;
zone_name: string;
created_on: string;
modified_on: string;
data: any;
}

View File

@ -0,0 +1,5 @@
export interface ICflareWorkerRoute {
id: string;
pattern: string;
script: string;
}

View File

@ -1,5 +1,3 @@
import * as plugins from '../cloudflare.plugins';
export interface ICflareZone { export interface ICflareZone {
id: string; id: string;
name: string; name: string;
@ -41,19 +39,3 @@ export interface ICflareZone {
type: string; type: string;
checked_on: string; checked_on: string;
} }
export interface ICflareRecord {
id: string;
type: string;
name: string;
content: string;
proxiable: boolean;
proxied: boolean;
ttl: number;
locked: boolean;
zone_id: string;
zone_name: string;
created_on: string;
modified_on: string;
data: any;
}

View File

@ -1 +1,3 @@
export * from './cloudflare.interfaces'; export * from './cloudflare.api.record';
export * from './cloudflare.api.zone';
export * from './cloudflare.api.workerroute';

View File

@ -3,6 +3,7 @@
"rules": { "rules": {
"semicolon": [true, "always"], "semicolon": [true, "always"],
"no-console": false, "no-console": false,
"no-return-await": false,
"ordered-imports": false, "ordered-imports": false,
"object-literal-sort-keys": false, "object-literal-sort-keys": false,
"member-ordering": { "member-ordering": {