From fa0bb3d55ae031ebfec89b05c3cc6c71baeaed5a Mon Sep 17 00:00:00 2001
From: pwlin <160443+pwlin@users.noreply.github.com>
Date: Thu, 8 Aug 2024 20:14:47 +0100
Subject: [PATCH] fixed all the bugs when running the backend on wasmer
---
.github/workflows/ci.yml | 42 +++++++++++++++++++++++++++
.gitignore | 1 +
client/config.example.json | 2 +-
client/index.html | 2 +-
client/js/config.js | 2 +-
client/js/model.js | 7 ++---
package.json | 2 +-
server/index.php | 5 ++++
server/salad-api.php | 59 +++++++++++++++++++-------------------
wasmer.toml | 13 +++++++++
10 files changed, 97 insertions(+), 38 deletions(-)
create mode 100644 .github/workflows/ci.yml
create mode 100644 wasmer.toml
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..5597213
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,42 @@
+name: Continuous Integration
+
+on:
+ pull_request:
+ push:
+ branches:
+ - main
+
+env:
+ GITHUB_TOKEN: ${{ github.token }}
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ build_and_deploy:
+ name: Build and Deploy PHP site to Wasmer Edge
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v2
+ - name: Install Wasmer
+ uses: wasmerio/setup-wasmer@v2
+ - name: Install PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: '8.3'
+ # tools: composer
+ # - name: Get composer cache directory
+ # id: composer-cache
+ # run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
+ # - name: Cache dependencies
+ # uses: actions/cache@v4
+ # with:
+ # path: ${{ steps.composer-cache.outputs.dir }}
+ # key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
+ # restore-keys: ${{ runner.os }}-composer-
+ # - name: Install dependencies
+ # run: composer install --prefer-dist
+ - name: Deploy app to Wasmer Edge
+ run: wasmer deploy --token=${{ secrets.WASMER_CIUSER_PROD_TOKEN }} --non-interactive --no-wait --no-persist-id
diff --git a/.gitignore b/.gitignore
index 3878834..891158f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,3 +8,4 @@ client/feeds.json
client/Neo.toml
package-lock.json
server/cache
+app.yaml
diff --git a/client/config.example.json b/client/config.example.json
index 97209ca..9ded174 100644
--- a/client/config.example.json
+++ b/client/config.example.json
@@ -3,7 +3,7 @@
"main_color": "red",
"second_color": "black",
"api_key": "dev-api-key",
- "api_url": "http://127.0.0.1:8080/index.php",
+ "api_url": "http://127.0.0.1:8081/index.php",
"favicons_enabled": false,
"redirlinks_enabled": false
}
\ No newline at end of file
diff --git a/client/index.html b/client/index.html
index d2046c7..833d1a2 100644
--- a/client/index.html
+++ b/client/index.html
@@ -38,7 +38,7 @@
diff --git a/client/js/config.js b/client/js/config.js
index b147f00..2fa9f48 100644
--- a/client/js/config.js
+++ b/client/js/config.js
@@ -32,7 +32,7 @@ class Config {
constructor() {
this.config = null;
this.defaultConfig = {
- 'xml2json_endpoint': '/xml2json?url=__URL__&_t=__TS__',
+ 'xml2json_endpoint': '/xml2json?url=__URL__&_t=__TS__&api_key=__API_KEY__',
'favicon_endpoint': '/favicon?url=__URL__&api_key=__API_KEY__',
'revredir_endpoint': '/revredir?url=__URL__&api_key=__API_KEY__'
};
diff --git a/client/js/model.js b/client/js/model.js
index f93fce5..fe086db 100644
--- a/client/js/model.js
+++ b/client/js/model.js
@@ -38,13 +38,12 @@ class Model {
* @returns {Promise} A promise that resolves to the normalized feed data.
*/
async fetchFeed(feed) {
- const apiKey = config.get('api_key');
- const serviceUrl = `${config.get('api_url')}${config.get('xml2json_endpoint').replace('__URL__', encodeURIComponent(feed.feedUrl)).replace('__TS__', utils.ts())}`;
- const response = await fetch(serviceUrl, {
+ const serviceUrl = `${config.get('api_url')}${config.get('xml2json_endpoint').replace('__URL__', encodeURIComponent(feed.feedUrl)).replace('__TS__', utils.ts()).replace('__API_KEY__', config.get('api_key'))}`;
+ const response = await fetch(serviceUrl/*, {
headers: {
'Authorization': `Bearer ${apiKey}`
}
- });
+ }*/);
const content = await response.json();
// Normalize the general structure of the feed entries
diff --git a/package.json b/package.json
index 6c45399..3879617 100644
--- a/package.json
+++ b/package.json
@@ -5,7 +5,7 @@
"dev-client": "node node_modules/vite/bin/vite.js --clearScreen false",
"build-client": "node node_modules/vite/bin/vite.js build",
"preview-client": "node node_modules/vite/bin/vite.js preview",
- "dev-server": "php -S localhost:8080 -t ./server"
+ "dev-server": "php -S localhost:8081 -t ./server"
},
"devDependencies": {
"vite": "^5.3.4",
diff --git a/server/index.php b/server/index.php
index 693b9a1..4c0f5a8 100644
--- a/server/index.php
+++ b/server/index.php
@@ -1,4 +1,9 @@
salad=$salad;}private function loginWithAPIKey($selectedApiKey,$accessLevelNeeded){$configApiKeys=$this->salad->config->get('api_keys');if(!empty($configApiKeys)){$apiKeys=['result'=>$configApiKeys];}else{$apiKeys=$this->salad->db->select('SELECT * FROM '.$this->salad->config->get('db_api_keys_table_name'));}$ret=false;foreach($apiKeys['result'] as $v){if( $v['key1']===$selectedApiKey && intval($v['access_level']) >= $accessLevelNeeded && in_array($v['enabled'],['yes','true',true]) ){$ret=true;if(!empty($v['tags_whitelist'])){$this->salad->request->setParam('tags_whitelist',$v['tags_whitelist']);}break;}} return $ret;}private function loginWithHttpDigest($accessLevelNeeded){$realm='Salad Restricted Area';if(empty($_SERVER['PHP_AUTH_DIGEST'])){$this->showLoginDialog($realm);return false;}else{$data=$this->httpDigestParse($_SERVER['PHP_AUTH_DIGEST']);$digestUsers=$this->salad->config->get('auth_digest_users',[]);$selectedUser=[];foreach($digestUsers as $u){if( $u['username']===@$data['username'] && $u['access_level'] >= $accessLevelNeeded && in_array($u['enabled'],['yes','true',true]) ){$selectedUser=$u;break;}} if(empty($selectedUser)){return false;}$A1=md5($data['username'].':'.$realm.':'.$selectedUser['password']);$A2=md5($_SERVER['REQUEST_METHOD'].':'.$data['uri']);$validResponse=md5($A1.':'.$data['nonce'].':'.$data['nc'].':'.$data['cnonce'].':'.$data['qop'].':'.$A2);if($data['response']!==$validResponse){return false;}else{return true;}}}public function login($args=[]){$args['access_level_needed']=intval($args['access_level_needed']);if($args['access_level_needed']!==0 && !isset($args['access_level_needed'])){$args['access_level_needed']=9;}if( $this->salad->request->getParam('is_cli')===true || $args['access_level_needed']===0 || @$args['skip_auth']===true ){return true;}$selectedApiKey=$this->apiKeyFromHeader();if(empty($selectedApiKey)){$selectedApiKey=@$args['api_key'];}if(!empty($selectedApiKey) && $this->salad->config->get('auth_api_keys_enabled')===true){return $this->loginWithAPIKey($selectedApiKey,$args['access_level_needed']);}elseif($this->salad->config->get('auth_digest_enabled')===true){return $this->loginWithHttpDigest($args['access_level_needed']);}else{return false;}} private function apiKeyFromHeader(){$apiKey='';$headers=getallheaders();if(isset($headers['Authorization'])){$authHeader=$headers['Authorization'];if(preg_match('/Bearer\s(\S+)/',$authHeader,$matches)){$apiKey=$matches[1];}} elseif(isset($headers['X-Api-Key'])){$apiKey=$headers['X-Api-Key'];}else if(isset($headers['X-Auth-Token'])){$apiKey=$headers['X-Auth-Token'];}return $apiKey;}private function showLoginDialog($realm){header('HTTP/1.1 401 Unauthorized');header('WWW-Authenticate:Digest realm="'.$realm.'",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"');}private function httpDigestParse($txt){$needed_parts=array('nonce'=>1,'nc'=>1,'cnonce'=>1,'qop'=>1,'username'=>1,'uri'=>1,'response'=>1);$data=array();$keys=implode('|',array_keys($needed_parts));preg_match_all('@('.$keys.')=(?:([\'"])([^\2]+?)\2|([^\s,]+))@',$txt,$matches,PREG_SET_ORDER);foreach($matches as $m){$data[$m[1]]=$m[3]?$m[3]:$m[4];unset($needed_parts[$m[1]]);}return $needed_parts?false:$data;}}
-class Cache{private int $ttl=86400;private $salad;public function __construct($salad){$this->salad=$salad;}public function makeCacheKey($key=[],$prefix=''){return $prefix.md5($prefix.serialize($key));}public function get($key,$default=false){if($this->salad->config->get('cache_type')==='none'){return false;}$cacheFile=$this->salad->config->get('cache_path').'/'.$key;if(is_file($cacheFile)){$data=json_decode(file_get_contents($cacheFile),true);if($data['expires']>0 && $data['expires']