Browse Source

aws migration #1

Sergio Álvarez 3 months ago
parent
commit
b81246210f
6 changed files with 282 additions and 302 deletions
  1. 1 0
      .gitignore
  2. 57 32
      api/main.php
  3. 213 213
      api/preg.php
  4. 1 0
      public/error.html
  5. 7 5
      src/components/App.js
  6. 3 52
      src/components/Footer.js

+ 1 - 0
.gitignore

@@ -19,3 +19,4 @@
 npm-debug.log*
 yarn-debug.log*
 yarn-error.log*
+*.zip

+ 57 - 32
api/main.php

@@ -1,47 +1,72 @@
 <?php
 
-require_once dirname(__FILE__) ."/redis.php";
+// https://docs.aws.amazon.com/aws-sdk-php/v3/api/api-dynamodb-2012-08-10.html
+// https://github.com/aws/aws-sdk-php/blob/master/src/DynamoDb
+use Aws\DynamoDb\DynamoDbClient;
+use Aws\DynamoDb\Marshaler;
+
+$ddb = DynamoDbClient::factory(array(
+    'version' => 'latest',
+    'region'  => 'eu-west-3'
+));
+
+$m = new Marshaler(['nullify_invalid' => true]);
+
 require_once dirname(__FILE__) ."/preg.php";
 
-function response($data) {
-	header("Content-Type: application/json");
-	die(json_encode($data));
+
+function response(array $data): string
+{
+    header("Content-Type: application/json");
+    // AWS API Gateway expected response
+    // https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-integration-settings-integration-response.html
+    return (json_encode([
+        'statusCode' => 200, 
+        'headers' => [
+            'Access-Control-Allow-Origin' => 'https://re.xrg.es'
+        ],
+        'body' => json_encode($data)
+    ]));
 }
 
-try {
-	if ($_SERVER['HTTP_HOST'] !== 'xrg.es') { // dev
-		// $ sudo mount --bind api/ ~/www/xrg.es
-		header("Access-Control-Allow-Origin: *");
-		header("Access-Control-Allow-Headers: *");
-		header("Access-Control-Allow-Methods: POST, GET, PUT, DELETE, OPTIONS");
 
-	} else if (!preg_match("~^https://".preg_quote($_SERVER["HTTP_HOST"])."/~", $_SERVER["HTTP_REFERER"])) {
-		throw new Exception("Invalid request");
-	}
+function main(array $data): string
+{
+    global $ddb, $m;
+
+    try {
+        $post = $data['body']? json_decode($data['body'], true): $data;
+
+        if (!is_array($post)) {
+            return response(['fatal' => 'no data']);
+        }
 
-	// thank you php...
-	$post = json_decode(file_get_contents("php://input"), true);
+        if ($post['hash']) {
+            $key = mb_substr($post['hash'], 1, 10);
+            $state = $ddb->getItem([
+                'TableName' => 'xrges',
+                'Key' => ['hash' => ['S' => $key]],
+                'ConsistentRead' => true
+            ]);
+            return response($state['Item']? $m->unmarshalItem($state['Item']): []);
+        }
 
-	if ($post['hash']) {
-		$key = mb_substr($post['hash'], 1, 10);
-		$state = $redis->hGetAll("xrg.es:". $key);
-		$redis->expire("xrg.es:". $key, 60*60*24*7); // touch
-		response($state);
-	}
+        $preg = new Preg($post);
+        $result = $preg->exec();
 
-	if (!is_array($post)) {
-		die("{}");
-	}
+        $key = $preg->hash();
 
-	$preg = new Preg($post);
-	$result = $preg->exec();
+        $post['hash'] = $key;
+        $post['ttl_expire'] = time() + (60*60*24*365*2);
 
-	$key = $preg->hash();
-	$redis->hMSet("xrg.es:". $key, $post);
-	$redis->expire("xrg.es:". $key, 60*60*24*7);
+        $ddb->putItem([
+            'TableName' => 'xrges',
+            'Item' => $m->marshalItem($post)
+        ]);
 
-	response(array_merge((array)$result, ['hash' => $key]));
+        return response(array_merge((array)$result, ['hash' => $key]));
 
-} catch (Throwable $e) {
-	response(['fatal' => $e->getMessage()]);
+    } catch (Throwable $e) {
+        return response(['fatal' => $e->getMessage()]);
+    }
 }

+ 213 - 213
api/preg.php

@@ -2,217 +2,217 @@
 
 class Preg 
 {
-	const FUNCTIONS = ["preg_match", "preg_match_all", "preg_split", "preg_replace", "preg_quote"];
-
-	const FLAGS = [
-		"preg_match" => ['PREG_OFFSET_CAPTURE', 'PREG_UNMATCHED_AS_NULL'], 
-		"preg_match_all" => ['PREG_SET_ORDER', 'PREG_OFFSET_CAPTURE', 'PREG_UNMATCHED_AS_NULL'], 
-		"preg_split" => ['PREG_SPLIT_NO_EMPTY', 'PREG_SPLIT_DELIM_CAPTURE', 'PREG_SPLIT_OFFSET_CAPTURE'], 
-		"preg_replace" => [], 
-		"preg_quote" => []
-	];
-
-	public function __construct($params) 
-	{
-		if (!in_array($params["method"], self::FUNCTIONS)) {
-			throw new Error("Invalid function");
-		}
-		ksort($params);
-		$this->hash = base_convert(crc32(serialize($params)), 10, 36);
-		$this->raw = $params;
-
-		$this->method = $params["method"];
-		
-		$this->pattern = $params["pattern"];
-		$this->replacement = $params["replacement"];
-		$this->subject = $params["subject"];
-		
-		$this->offset = mb_strlen($params["offset"])? (int)$params["offset"]: 0;
-		$this->limit = mb_strlen($params["limit"])? (int)$params["limit"]: -1;
-		$this->delimeter = mb_strlen($params["delimeter"])? $params["delimeter"]: null;
-
-		$this->flags = 0;
-		$this->flags_str = [];
-		foreach (array_intersect(self::FLAGS[$this->method], array_keys((array)$params)) as $flag) {
-			if ($params[$flag]) {
-				$this->flags |= constant($flag);
-				$this->flags_str[] = $flag;
-			}
-		}
-	}
-
-	public function exec() 
-	{
-		if (!mb_strlen($this->pattern)) {
-			return [];
-		}
-
-		$func = "exec_". $this->method;
-		return $this->$func();
-	}
-
-	protected function exec_preg_match() 
-	{
-		$ret = @preg_match($this->pattern, $this->subject, $matches, $this->flags, $this->offset);
-
-		$error = $this->last_error();
-
-		return array_merge($this->result($ret), [
-			"dump" => $error?: $matches, 
-			"error" => (bool)$error, 
-			"code" => $this->syntax()
-		]);
-	}
-
-	protected function exec_preg_match_all() 
-	{
-		$ret = @preg_match_all($this->pattern, $this->subject, $matches, $this->flags, $this->offset);
-
-		$error = $this->last_error();
-		
-		return array_merge($this->result($ret), [
-			"dump" => $error?: $matches, 
-			"error" => (bool)$error, 
-			"code" => $this->syntax()
-		]);
-	}
-
-	protected function exec_preg_split() 
-	{
-		$ret = @preg_split($this->pattern, $this->subject, $this->limit, $this->flags);
-
-		$error = $this->last_error();
-		
-		return array_merge($this->result($ret), [
-			"dump" => $error?: $ret, 
-			"error" => (bool)$error, 
-			"code" => $this->syntax()
-		]);
-	}
-
-	protected function exec_preg_replace() 
-	{
-		$ret = @preg_replace($this->pattern, $this->replacement, $this->subject, $this->limit);
-
-		$error = $this->last_error();
-		
-		return array_merge($this->result($ret), [
-			"dump" => $error?: $ret, 
-			"error" => (bool)$error, 
-			"code" => $this->syntax()
-		]);
-	}
-
-	protected function exec_preg_quote() 
-	{
-		$ret = @preg_quote($this->pattern, $this->delimeter);
-
-		$error = $this->last_error();
-		
-		return array_merge($this->result($ret), [
-			"dump" => $error?: $ret, 
-			"error" => (bool)$error, 
-			"code" => $this->syntax()
-		]);
-	}
-
-
-	protected function last_error() 
-	{
-		/*if (!preg_last_error()) {
-			return false;
-		}*/
-
-		$ple = preg_last_error();
-		$e = error_get_last();
-		$gdc = get_defined_constants(true)['pcre'];
-		$return = [];
-
-		if ($ple and in_array($ple, $gdc)) {
-			$return["preg_last_error()"] = array_flip($gdc)[preg_last_error()];
-		}
-
-		if ($e['type'] < E_NOTICE) {
-			$return["php error message"] = $e['message'];
-		}
-
-		return $return; 
-	}
-
-	protected function result($var) 
-	{
-		$gettype = gettype($var);
-		$return = [
-			"return_value" => "??",
-			"return_type" => $gettype
-		];
-
-		if (in_array($gettype, ["boolean", "integer", "NULL"])) {
-			$return["return_value"] = json_encode($var);
-
-		} elseif (in_array($gettype, ["string"])) {
-			$return["return_value"] = "len(". mb_strlen($var). ")";
-
-		} elseif (in_array($gettype, ["array"])) {
-			$return["return_value"] = "count(". count($var). ")";
-		}
-
-		return $return;
-	}
-
-	protected function syntax() 
-	{
-		$args = [$this->method .'("'. $this->pattern.'"'];
-
-		if (in_array($this->method, ['preg_match', 'preg_match_all'])) {
-			$args[] = '"'. $this->crop_text($this->subject) .'", $matches';
-
-			if (count($this->flags_str)) {
-				$args[] = implode(" | ", $this->flags_str);
-			}
-
-			if (mb_strlen($this->raw['offset'])) {
-				$args[] = (!count($this->flags_str)? 'null, ': ''). (string)$this->offset;
-			}
-
-		} elseif (in_array($this->method, ['preg_split'])) {
-			$args[] = '"'. $this->crop_text($this->subject) .'"';
-
-			if (mb_strlen($this->raw['limit'])) {
-				$args[] = (string)$this->limit;
-			}
-
-			if (count($this->flags_str)) {
-				$args[] = (!mb_strlen($this->raw['limit'])? 'null, ': ''). implode(" | ", $this->flags_str);
-			}
-
-		} elseif (in_array($this->method, ['preg_replace'])) {
-			$args[] = '"'. $this->crop_text($this->replacement) .'"';
-			$args[] = '"'. $this->crop_text($this->subject) .'"';
-
-			if (mb_strlen($this->raw['limit'])) {
-				$args[] = (string)$this->limit;
-			}
-
-		} elseif (in_array($this->method, ['preg_quote'])) {
-			if (mb_strlen($this->delimeter)) {
-				$args[] = (string)$this->delimeter;
-			}
-		}
-		$code = implode(", ", $args). ");";
-
-		return str_replace(array('?&gt;', '&lt;?php&nbsp;', '&nbsp;', '<code>', '</code>'), array('', '', ' ', '', ''), highlight_string('<?php '. $code .' ?'.'>', true) );
-	}
-
-	protected function crop_text($str, $len=50) 
-	{
-		if (mb_strlen($str, "utf-8") < $len) {
-			return $str;
-		}
-		return str_replace(array("\r", "\n"), array('\r', '\n'), addslashes(mb_substr($str, 0, $len, "utf-8")). "...");
-	}
-
-	public function hash() {
-		return $this->hash; 
-	}
+    const FUNCTIONS = ["preg_match", "preg_match_all", "preg_split", "preg_replace", "preg_quote"];
+
+    const FLAGS = [
+        "preg_match" => ['PREG_OFFSET_CAPTURE', 'PREG_UNMATCHED_AS_NULL'], 
+        "preg_match_all" => ['PREG_SET_ORDER', 'PREG_OFFSET_CAPTURE', 'PREG_UNMATCHED_AS_NULL'], 
+        "preg_split" => ['PREG_SPLIT_NO_EMPTY', 'PREG_SPLIT_DELIM_CAPTURE', 'PREG_SPLIT_OFFSET_CAPTURE'], 
+        "preg_replace" => [], 
+        "preg_quote" => []
+    ];
+
+    public function __construct($params) 
+    {
+        if (!in_array($params["method"], self::FUNCTIONS)) {
+            throw new Error("Invalid function");
+        }
+        ksort($params);
+        $this->hash = base_convert(crc32(serialize($params)), 10, 36);
+        $this->raw = $params;
+
+        $this->method = $params["method"];
+        
+        $this->pattern = $params["pattern"];
+        $this->replacement = $params["replacement"];
+        $this->subject = $params["subject"];
+        
+        $this->offset = mb_strlen($params["offset"])? (int)$params["offset"]: 0;
+        $this->limit = mb_strlen($params["limit"])? (int)$params["limit"]: -1;
+        $this->delimeter = mb_strlen($params["delimeter"])? $params["delimeter"]: null;
+
+        $this->flags = 0;
+        $this->flags_str = [];
+        foreach (array_intersect(self::FLAGS[$this->method], array_keys((array)$params)) as $flag) {
+            if ($params[$flag]) {
+                $this->flags |= constant($flag);
+                $this->flags_str[] = $flag;
+            }
+        }
+    }
+
+    public function exec() 
+    {
+        if (!mb_strlen($this->pattern)) {
+            return [];
+        }
+
+        $func = "exec_". $this->method;
+        return $this->$func();
+    }
+
+    protected function exec_preg_match() 
+    {
+        $ret = @preg_match($this->pattern, $this->subject, $matches, $this->flags, $this->offset);
+
+        $error = $this->last_error();
+
+        return array_merge($this->result($ret), [
+            "dump" => $error?: $matches, 
+            "error" => (bool)$error, 
+            "code" => $this->syntax()
+        ]);
+    }
+
+    protected function exec_preg_match_all() 
+    {
+        $ret = @preg_match_all($this->pattern, $this->subject, $matches, $this->flags, $this->offset);
+
+        $error = $this->last_error();
+        
+        return array_merge($this->result($ret), [
+            "dump" => $error?: $matches, 
+            "error" => (bool)$error, 
+            "code" => $this->syntax()
+        ]);
+    }
+
+    protected function exec_preg_split() 
+    {
+        $ret = @preg_split($this->pattern, $this->subject, $this->limit, $this->flags);
+
+        $error = $this->last_error();
+        
+        return array_merge($this->result($ret), [
+            "dump" => $error?: $ret, 
+            "error" => (bool)$error, 
+            "code" => $this->syntax()
+        ]);
+    }
+
+    protected function exec_preg_replace() 
+    {
+        $ret = @preg_replace($this->pattern, $this->replacement, $this->subject, $this->limit);
+
+        $error = $this->last_error();
+        
+        return array_merge($this->result($ret), [
+            "dump" => $error?: $ret, 
+            "error" => (bool)$error, 
+            "code" => $this->syntax()
+        ]);
+    }
+
+    protected function exec_preg_quote() 
+    {
+        $ret = @preg_quote($this->pattern, $this->delimeter);
+
+        $error = $this->last_error();
+        
+        return array_merge($this->result($ret), [
+            "dump" => $error?: $ret, 
+            "error" => (bool)$error, 
+            "code" => $this->syntax()
+        ]);
+    }
+
+
+    protected function last_error() 
+    {
+        /*if (!preg_last_error()) {
+            return false;
+        }*/
+
+        $ple = preg_last_error();
+        $e = error_get_last();
+        $gdc = get_defined_constants(true)['pcre'];
+        $return = [];
+
+        if ($ple and in_array($ple, $gdc)) {
+            $return["preg_last_error()"] = array_flip($gdc)[preg_last_error()];
+        }
+
+        if ($e['type'] < E_NOTICE) {
+            $return["php error message"] = $e['message'];
+        }
+
+        return $return; 
+    }
+
+    protected function result($var) 
+    {
+        $gettype = gettype($var);
+        $return = [
+            "return_value" => "??",
+            "return_type" => $gettype
+        ];
+
+        if (in_array($gettype, ["boolean", "integer", "NULL"])) {
+            $return["return_value"] = json_encode($var);
+
+        } elseif (in_array($gettype, ["string"])) {
+            $return["return_value"] = "len(". mb_strlen($var). ")";
+
+        } elseif (in_array($gettype, ["array"])) {
+            $return["return_value"] = "count(". count($var). ")";
+        }
+
+        return $return;
+    }
+
+    protected function syntax() 
+    {
+        $args = [$this->method .'("'. $this->pattern.'"'];
+
+        if (in_array($this->method, ['preg_match', 'preg_match_all'])) {
+            $args[] = '"'. $this->crop_text($this->subject) .'", $matches';
+
+            if (count($this->flags_str)) {
+                $args[] = implode(" | ", $this->flags_str);
+            }
+
+            if (mb_strlen($this->raw['offset'])) {
+                $args[] = (!count($this->flags_str)? 'null, ': ''). (string)$this->offset;
+            }
+
+        } elseif (in_array($this->method, ['preg_split'])) {
+            $args[] = '"'. $this->crop_text($this->subject) .'"';
+
+            if (mb_strlen($this->raw['limit'])) {
+                $args[] = (string)$this->limit;
+            }
+
+            if (count($this->flags_str)) {
+                $args[] = (!mb_strlen($this->raw['limit'])? 'null, ': ''). implode(" | ", $this->flags_str);
+            }
+
+        } elseif (in_array($this->method, ['preg_replace'])) {
+            $args[] = '"'. $this->crop_text($this->replacement) .'"';
+            $args[] = '"'. $this->crop_text($this->subject) .'"';
+
+            if (mb_strlen($this->raw['limit'])) {
+                $args[] = (string)$this->limit;
+            }
+
+        } elseif (in_array($this->method, ['preg_quote'])) {
+            if (mb_strlen($this->delimeter)) {
+                $args[] = (string)$this->delimeter;
+            }
+        }
+        $code = implode(", ", $args). ");";
+
+        return str_replace(array('?&gt;', '&lt;?php&nbsp;', '&nbsp;', '<code>', '</code>'), array('', '', ' ', '', ''), highlight_string('<?php '. $code .' ?'.'>', true) );
+    }
+
+    protected function crop_text($str, $len=50) 
+    {
+        if (mb_strlen($str, "utf-8") < $len) {
+            return $str;
+        }
+        return str_replace(array("\r", "\n"), array('\r', '\n'), addslashes(mb_substr($str, 0, $len, "utf-8")). "...");
+    }
+
+    public function hash() {
+        return $this->hash; 
+    }
 }

+ 1 - 0
public/error.html

@@ -0,0 +1 @@
+Page not found. Go back and try again.

+ 7 - 5
src/components/App.js

@@ -9,7 +9,7 @@ import Result from './Result';
 import Footer from './Footer';
 import Help from './Help';
 
-/*
+/* testing
 
 ~(?P<fsda>\d+)~
 ~(?P<fsda>\w+)~
@@ -42,8 +42,8 @@ export default class App extends Component {
     }
 
     this.timer = null
-    this.apiUrl = window.location.hostname === 'xrg.es'? 
-      'https://xrg.es/api': 
+    this.apiUrl = window.location.hostname === 're.xrg.es'? 
+      'https://9eevjizn12.execute-api.eu-west-3.amazonaws.com/default/xrges-api': 
       'http://'+ window.location.hostname +'/xrg.es/main.php' // dev
   }
 
@@ -63,6 +63,8 @@ export default class App extends Component {
       this.setState({ loading: true }, () => {
         axios.post(this.apiUrl, { hash })
         .then((res) => {
+          delete res.data['hash']
+          delete res.data['ttl_expire']
           this.setState({ ...res.data, response: {}, loading: false }, () => { this.api() })
         })
       })
@@ -101,14 +103,14 @@ export default class App extends Component {
   render() {
     const { method, pattern, replacement, subject, response, spin, ...state } = this.state
     const hash = response.hash? response.hash: ''
-    const clipb = hash? <Clipboard className="btn tooltip tooltip-bottom" data-tooltip="Copy permalink" data-clipboard-text={"https://xrg.es/#"+hash}><i className="icon icon-link"></i></Clipboard>: <span />
+    const clipb = hash? <Clipboard className="btn tooltip tooltip-bottom" data-tooltip="Copy permalink" data-clipboard-text={window.location.origin+"/#"+hash}><i className="icon icon-link"></i></Clipboard>: <span />
 
     return (
       <div className="container">
         <header className="navbar">
           <section className="navbar-section">
             <svg><use xlinkHref="/img/symbol-defs.svg#icon-php-alt" /></svg>
-            <a href="https://xrg.es" className="navbar-brand">Regular Expressions tester</a>
+            <a href={window.location.origin} className="navbar-brand">Regular Expressions tester</a>
           </section>
           <section className="navbar-section">
             {clipb}

+ 3 - 52
src/components/Footer.js

@@ -1,63 +1,14 @@
 import React, { Component } from 'react';
 
-class IconLink extends Component {
-  constructor(props) {
-    super(props)
-
-    this.state = {
-      spin: 10,
-      noanimate: false
-    }
-  }
-
-  componentDidMount() {
-    if (this.state.noanimate) {
-      return
-    }
-    //this.tick()
-  }
-
-  componentWillUnmount() {
-    clearInterval(this.timerID)
-  }
-
-  tick = () => {
-    this.setState({ spin: this.state.spin + (Math.floor(Math.random() * Math.floor(720))) - 360 }, () => {
-      clearInterval(this.timerID)
-      this.timerID = setTimeout(() => this.tick(), 60000)
-    })
-  }
-
-  render() {
-    const css = {
-      transform: 'rotate('+this.state.spin+'deg)' 
-    }
-
-    return (
-      <span>
-        <a href={this.props.href} className="tooltip" data-tooltip={this.props.alt} onMouseEnter={this.tick}>
-          <svg style={css}><use xlinkHref={'/img/symbol-defs.svg#'+this.props.src} /></svg>
-          <span>{this.props.alt}</span>
-        </a>
-      </span>
-    )
-  }
-}
-
 export default class Footer extends Component {
   render() {
     return (
       <footer className="columns">
         <div className="column col-12">
           <p className="text-right">
-            <small>
-              <span>Coded by <a href="https://twitter.com/xergio">Sergio Álvarez</a> – </span>
-              <span><IconLink href="https://reactjs.org/" src="icon-react" alt="React" />  </span>
-              <span><IconLink href="https://picturepan2.github.io/spectre/" src="icon-spectre" alt="Spectre" /> </span>
-              <span><IconLink href="https://php.net/manual/es/ref.pcre.php" src="icon-php" alt="PHP" /> </span>
-              <span><IconLink href="https://redis.io/" src="icon-redis" alt="Redis" /> </span>
-              <span><IconLink href="https://gitlab.com/xergio/xrg.es/issues" src="icon-gitlab" alt="Gitlab" /> </span>
-            </small></p>
+            <small>Coded by <a href="https://twitter.com/xergio">Sergio Álvarez</a></small>
+            <small> – <a href="https://sergio.am/code/xrg.es">Source code</a>.</small>
+          </p>
         </div>
       </footer>
     )