Untitled

 avatar
unknown
plain_text
2 years ago
11 kB
7
Indexable
const clientId = '746239575955-c4d2o1ahg4ts6ahm3a5lh5lp9g8m15h4.apps.googleusercontent.com';
const clientSecret = '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';
const refreshToken = '';
const service_account = true
const service_account_json = {
  "type": "service_account",
  "project_id": "gcp-web-bill",
  "private_key_id": "2ffeb836b95626dc159e7656fa1a4a9b79b986f4",
  "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCT7lBxMBMGXYF4\n4zOTNYDPxSEeHS42ruGRq2u8OvusgJl92WOwvMQpAMC/2v67fZpqKIeC87H9sR7n\nBsbOWX92dF5RQB4kbiZRR2tu9eaK2TAXrKeRdC8dp7Iv/6k0M5wAnkJmOUWLkZYO\nen2xM95HmCcWF3MSgq9kyuqouLL7EBEcFdT6j3lz2wyLEMt4u4h17/NH5kfEdmjF\n+hG/Jk0cAyE/vdcY2m7dH6rG+1utp75Ft9ZQUX/hVzxo5d5Xmmexx4qz63FmzSAh\nHRHi8xxyRuAzIYpbVJzcqlPTp1wqpvF2AE4yKynNBGmcfJz0duG/zndESy2W93xw\nnUUdE5mlAgMBAAECggEACpDBEJB7L19yzZhGmPs/h7df4ed6I0AyzqVkZLV+v6eQ\nrucdomQdC4OpAJrqyifSpUXfRdhTbFff6ea3QMouGx79FIcEkBwI4xG7h9mKvXx9\nHZfAgRg//Dl7tcdoBZkxCW9ENUpeX/VGNyMDAqq+c7UXh5Glr5sDXWrLv0Cv0dQM\nUlnSYjWIaqK2gV+t0DRr21vkZFvDKVTEVQFgWM0T+O736j4u63Ghw0llUs4h7fB9\nMluJx/61LdJShhto750Rj720AS/ZTDSlmXGn5dYO8nBxPO0IAiIklv+IYDZrXMX+\nU6/Lr1VbEUL9zHHfrKxSLLMHVUy5/SurpbGPNqMzSQKBgQDQ/1KMYkSVr//+l1Tx\nkPeXA2OG8djDN0uXZ+SpvLSkWeEXw2VjP9qQ6CHH/j2cR5beuflP4OCZhnDVblum\ns/5uZs42H62T2iXlpR01y2NHFM/OJOKoHu/MkwR6YxmtM6CsuoIdbA/86Qx4R0y/\n2DxvSoB9+07t9sWNq9fgw+1RKQKBgQC1MzFBrLX/Tpa5ArIXk7faUyDDAmgPN9Z8\nxXQ1nWCKD9AI2F+NuUZ2lGkXC4zMj9dpZ9p3F6WrDbc5oQ243I6iDkPSPXsNlGwr\n8epR2DQV7eK44yywRAPcFGz9e2TIiV/uQRzYeub9qE9WXcAiihBer5hhbaAzG13Y\nwzN3A8soHQKBgA6oGS3nJlApC5bbHgXi2LoqbqxkAYOPOSnhC1Qr+68a7oIwHcgc\nDkPjQ5s25TM0ggwUlrtXVejuWLNfDeZe8RTk+Mkq541a2xWDwsym+stdVkVqqMqJ\ngN+j38iFcqOG31m9qqpNjcF6xnkY5vT9QR+85c3v9dUSXUBWMMl+dAIhAoGBAKYW\nWVTQYTW2LFNC1EjmHoRg48u7nfQtA030kxM9wKpr2gzxf4xhQeQZHsZ/0LwEMLet\n+MJmi3ThqouJQxGqIJcMKkBAGFWXMRuKr3P7j6p2FPXRTTn37e3BTzF89CEYQjUZ\nu7310Vq59pa8cgt2r5CKtUrAeDHAKOIx8i9p68JFAoGAfUsqAQgrIDYDMpmSgwo9\nYBALum6m9FjPw3GCqIEPq7WS9rHKe4TWLWT1h9GMJ3j6Tq5GNCvIan9q/k9lmpWS\nJWD8uwgeY5u/EByVlE2sHzFNGWkhUEjJwLPNlOE1hScYfb8b8c3YDixJKVMLvx+G\nV5KDunuVaU5MECrqQW7SoAM=\n-----END PRIVATE KEY-----\n",
  "client_email": "gcp-web-billing-hook@gcp-web-bill.iam.gserviceaccount.com",
  "client_id": "113565551544602521875",
  "auth_uri": "https://accounts.google.com/o/oauth2/auth",
  "token_uri": "https://oauth2.googleapis.com/token",
  "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
  "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/gcp-web-billing-hook%40gcp-web-bill.iam.gserviceaccount.com",
  "universe_domain": "googleapis.com"
}
var billing_ids = [{
		"name": "GCP Main Billing",
		"id": "01924B-D2C7F3-5DE8E7"
	}
]
const html = `<!DOCTYPE html>
<html>
   <head>
      <title>GCP Server Current State</title>
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <!-- Bootstrap CSS -->
      <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootswatch@4/dist/flatly/bootstrap.min.css">
   </head>
   <body>
      <header>
         <nav class="navbar navbar-dark bg-dark navbar-expand-lg">
            <a href="/" class="navbar-brand d-flex align-items-center">
            <img src="https://images.cdn.hashhackers.com/logo/logo-white-d.svg" class="btn1" width="220" height="40">
            </a>
            <div class="collapse navbar-collapse" id="navbarNav">
               <ul class="navbar-nav ">
               </ul>
            </div>
         </nav>
      </header>
      <div class="container mt-5 mb-5">
         <marquee>Web services which are inactive need not to be reported, they'll be back online in a time frame of 24 to 48 hours.</marquee>
         <ul id="billing-list" class="list-group">
            <li class="list-group-item active text-center">Billing Status</li>
            <!-- JavaScript code will populate the rest of the list -->
         </ul>
      </div>
      <!-- Bootstrap JavaScript -->
      <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/2.9.3/umd/popper.min.js"></script>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.6.0/js/bootstrap.min.js"></script>
      <script>
         const billing_ids = ` + billing_ids.length + `;
           const billing_names = ${JSON.stringify(billing_ids.map(billing => billing.name))}
         
         // Generate an array of numbers from 0 to billing_ids - 1
         const indexArray = Array.from({ length: billing_ids }, (_, i) => i);
         
         // Loop through the indexArray and send requests to the API endpoint using each number as an index
         for (const index of indexArray) {
         	const listItem = $('<li class="text-wrap">').addClass('list-group-item d-flex justify-content-between align-items-center');
         	const nameSpan = $('<span class="d-inline-block text-break">').text(billing_names[index]).appendTo(listItem);
            const statusSpan = $('<span>').text('Checking...').addClass('badge badge-secondary').appendTo(listItem);
         
			
         	// Send a request to the API endpoint with the current index and set the status badge
         	$.getJSON('/'+index, (data) => {
         		if (data.status == 'invalid') {
         			statusSpan.text('API Error').addClass('badge-info').removeClass('badge-secondary');
         		} else if (data.status) {
                statusSpan.text('Active').addClass('badge-success').removeClass('badge-secondary');
         			
         		} else {
                statusSpan.text('Inactive').addClass('badge-danger').removeClass('badge-secondary');
                
              }
         	});
         
         	$('#billing-list').append(listItem);
         }
      </script>
   </body>
</html>`

async function handleRequest(request) {
	let requrl = new URL(request.url);
	let path = requrl.pathname;
	if (path == '/') {
		return new Response(html, {
			status: 200,
			headers: {
				"content-type": "text/html;",
				"Access-Control-Allow-Origin": "*",
			}
		});
	}
	let billingAccountId = path.slice(1);
	const accessToken = await fetchAccessToken();

	// Build the API request URL
	const url = 'https://cloudbilling.googleapis.com/v1/billingAccounts/' + billing_ids[billingAccountId].id;

	// Send the API request to check if the billing account is active
	const response = await fetch(url, {
		headers: {
			Authorization: `Bearer ${accessToken}`
		}
	});
	var text = await response.text()
	if (!response.ok) {
		return new Response('{"status":"invalid"}', {
			status: 200,
			headers: {
				"content-type": "application/json;",
				"Access-Control-Allow-Origin": "*",
			}
		});
	}

	const data = JSON.parse(text);;

	// Check if the billing account is active
	const billingEnabled = data.open;

	// Return a response based on whether the billing account is active or not
	if (billingEnabled) {
		return new Response('{"status":' + billingEnabled + '}', {
			status: 200,
			headers: {
				"content-type": "application/json;",
				"Access-Control-Allow-Origin": "*",
			}
		});
	} else {
		let json = {
			chat_id: "513611281",
			text: "Web Service Offline: " + billing_ids[billingAccountId].name,
		}
		await fetch("https://api.telegram.org/bot929918166:AAFbDTrQZASGrcmyuplm3Sw4ym-dAv4EKac/SendMessage", {
		  method: "POST",
		  headers: {
		    'content-type': 'application/json',
		  },
		  body: JSON.stringify(json)
		});
		return new Response('{"status":' + billingEnabled + '}', {
			status: 200,
			headers: {
				"content-type": "application/json;",
				"Access-Control-Allow-Origin": "*",
			}
		});
	}
}

async function fetchAccessToken() {
	const url = "https://oauth2.googleapis.com/token";
	const headers = {
		'Content-Type': 'application/x-www-form-urlencoded'
	};
	var post_data;
	if (service_account) {
		console.log('Service Account')
		const jwttoken = await JSONWebToken.generateGCPToken(service_account_json);
		post_data = {
			grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
			assertion: jwttoken,
		};
	} else {
		console.log('Refresh Token')
		post_data = {
			client_id: clientId,
			client_secret: clientSecret,
			refresh_token: refreshToken,
			grant_type: "refresh_token",
		};
	}

	let requestOption = {
		'method': 'POST',
		'headers': headers,
		'body': await enQuery(post_data)
	};

	let response;
	for (let i = 0; i < 3; i++) {
		response = await fetch(url, requestOption);
		if (response.ok) {
			break;
		}
		await this.sleep(800 * (i + 1));
	}
	const data = await response.json();
	return data.access_token;
}

const JSONWebToken = {
	header: {
		alg: 'RS256',
		typ: 'JWT'
	},
	importKey: async function(pemKey) {
		var pemDER = this.textUtils.base64ToArrayBuffer(pemKey.split('\n').map(s => s.trim()).filter(l => l.length && !l.startsWith('---')).join(''));
		return crypto.subtle.importKey('pkcs8', pemDER, {
			name: 'RSASSA-PKCS1-v1_5',
			hash: 'SHA-256'
		}, false, ['sign']);
	},
	createSignature: async function(text, key) {
		const textBuffer = this.textUtils.stringToArrayBuffer(text);
		return crypto.subtle.sign('RSASSA-PKCS1-v1_5', key, textBuffer)
	},
	generateGCPToken: async function(serviceAccount) {
		const iat = parseInt(Date.now() / 1000);
		var payload = {
			"iss": serviceAccount.client_email,
			"scope": "https://www.googleapis.com/auth/cloud-billing",
			"aud": "https://oauth2.googleapis.com/token",
			"exp": iat + 3600,
			"iat": iat
		};
		const encPayload = btoa(JSON.stringify(payload));
		const encHeader = btoa(JSON.stringify(this.header));
		var key = await this.importKey(serviceAccount.private_key);
		var signed = await this.createSignature(encHeader + "." + encPayload, key);
		return encHeader + "." + encPayload + "." + this.textUtils.arrayBufferToBase64(signed).replace(/\//g, '_').replace(/\+/g, '-');
	},
	textUtils: {
		base64ToArrayBuffer: function(base64) {
			var binary_string = atob(base64);
			var len = binary_string.length;
			var bytes = new Uint8Array(len);
			for (var i = 0; i < len; i++) {
				bytes[i] = binary_string.charCodeAt(i);
			}
			return bytes.buffer;
		},
		stringToArrayBuffer: function(str) {
			var len = str.length;
			var bytes = new Uint8Array(len);
			for (var i = 0; i < len; i++) {
				bytes[i] = str.charCodeAt(i);
			}
			return bytes.buffer;
		},
		arrayBufferToBase64: function(buffer) {
			let binary = '';
			let bytes = new Uint8Array(buffer);
			let len = bytes.byteLength;
			for (let i = 0; i < len; i++) {
				binary += String.fromCharCode(bytes[i]);
			}
			return btoa(binary);
		}
	}
};

function enQuery(data) {
	const ret = [];
	for (let d in data) {
		ret.push(encodeURIComponent(d) + '=' + encodeURIComponent(data[d]));
	}
	return ret.join('&');
}

function sleep(ms) {
	return new Promise(function(resolve, reject) {
		let i = 0;
		setTimeout(function() {
			//console.log('sleep' + ms);
			i++;
			if (i >= 2) reject(new Error('i>=2'));
			else resolve(i);
		}, ms);
	})
}


addEventListener('fetch', event => {
	event.respondWith(handleRequest(event.request))
})
Editor is loading...