diff --git a/.env.example b/.env.example index 21b7119..362b5d7 100644 --- a/.env.example +++ b/.env.example @@ -1,8 +1,6 @@ # General -INTERFACE_EDITOR_DOMAIN=localhost:3000 INTERFACE_EDITOR_URL=http://localhost:3000 ACCESS_CONTROL_ALLOW_ORIGIN=http://localhost:3000 -PUBLIC_FILE_PATH=http://localhost:9200/private/files/ JSONMS_CYPHER_KEY=urjMdK071cL935eKdczjEQ== # Database diff --git a/.github/workflows/deploy-dev.yml b/.github/workflows/deploy-dev.yml new file mode 100644 index 0000000..f249f64 --- /dev/null +++ b/.github/workflows/deploy-dev.yml @@ -0,0 +1,56 @@ +name: Deploy DEV to host + +on: + push: + branches: + - dev # Change this to your default branch + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Set up PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.3' + tools: composer + + - name: Install dependencies + run: composer install --no-dev --optimize-autoloader + + - name: Zip the folder + run: zip -r archive.zip . -x "./.git/*" "./.github/*" + + - name: Deploy via SSH + uses: appleboy/scp-action@v0.1.7 + with: + host: ${{ secrets.FTP_SERVER }} + username: ${{ secrets.FTP_USERNAME }} + password: ${{ secrets.FTP_PASSWORD }} + source: "archive.zip" + target: ${{ secrets.FTP_SERVER_DEV_PATH }} + + - name: SSH into server and unzip + uses: appleboy/ssh-action@v1.0.3 + with: + host: ${{ secrets.FTP_SERVER }} + username: ${{ secrets.FTP_USERNAME }} + password: ${{ secrets.FTP_PASSWORD }} + script: | + cd ${{ secrets.FTP_SERVER_DEV_PATH }} + unzip -o archive.zip -d . + rm archive.zip + echo "INTERFACE_EDITOR_URL=https://dev.json.ms" > .env + echo "ACCESS_CONTROL_ALLOW_ORIGIN=https://dev.json.ms" >> .env + echo "JSONMS_CYPHER_KEY=${{ secrets.JSONMS_CYPHER_KEY }}" >> .env + echo "GOOGLE_OAUTH_CLIENT_ID=637442439591-qrrpb3v9d3n5m8b8gheorfa1fbi5o6qc.apps.googleusercontent.com" >> .env + echo "GOOGLE_OAUTH_CLIENT_SECRET=${{ secrets.GOOGLE_OAUTH_CLIENT_SECRET }}" >> .env + echo "GOOGLE_OAUTH_CALLBACK_URL=https://server.dev.json.ms/google/callback" >> .env + echo "DATABASE_HOST=${{ secrets.DATABASE_HOST }}" >> .env + echo "DATABASE_DBNAME=${{ secrets.DATABASE_DEV_DBNAME }}" >> .env + echo "DATABASE_USERNAME=${{ secrets.DATABASE_USERNAME }}" >> .env + echo "DATABASE_PASSWORD=${{ secrets.DATABASE_PASSWORD }}" >> .env diff --git a/.github/workflows/deploy-prod.yml b/.github/workflows/deploy-prod.yml new file mode 100644 index 0000000..491c771 --- /dev/null +++ b/.github/workflows/deploy-prod.yml @@ -0,0 +1,56 @@ +name: Deploy DEV to host + +on: + push: + branches: + - master # Change this to your default branch + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Set up PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.3' + tools: composer + + - name: Install dependencies + run: composer install --no-dev --optimize-autoloader + + - name: Zip the folder + run: zip -r archive.zip . -x "./.git/*" "./.github/*" + + - name: Deploy via SSH + uses: appleboy/scp-action@v0.1.7 + with: + host: ${{ secrets.FTP_SERVER }} + username: ${{ secrets.FTP_USERNAME }} + password: ${{ secrets.FTP_PASSWORD }} + source: "archive.zip" + target: ${{ secrets.FTP_SERVER_PROD_PATH }} + + - name: SSH into server and unzip + uses: appleboy/ssh-action@v1.0.3 + with: + host: ${{ secrets.FTP_SERVER }} + username: ${{ secrets.FTP_USERNAME }} + password: ${{ secrets.FTP_PASSWORD }} + script: | + cd ${{ secrets.FTP_SERVER_PROD_PATH }} + unzip -o archive.zip -d . + rm archive.zip + echo "INTERFACE_EDITOR_URL=https://json.ms" > .env + echo "ACCESS_CONTROL_ALLOW_ORIGIN=https://json.ms" >> .env + echo "JSONMS_CYPHER_KEY=${{ secrets.JSONMS_CYPHER_KEY }}" >> .env + echo "GOOGLE_OAUTH_CLIENT_ID=637442439591-qrrpb3v9d3n5m8b8gheorfa1fbi5o6qc.apps.googleusercontent.com" >> .env + echo "GOOGLE_OAUTH_CLIENT_SECRET=${{ secrets.GOOGLE_OAUTH_CLIENT_SECRET }}" >> .env + echo "GOOGLE_OAUTH_CALLBACK_URL=https://server.json.ms/google/callback" >> .env + echo "DATABASE_HOST=${{ secrets.DATABASE_HOST }}" >> .env + echo "DATABASE_DBNAME=${{ secrets.DATABASE_PROD_DBNAME }}" >> .env + echo "DATABASE_USERNAME=${{ secrets.DATABASE_USERNAME }}" >> .env + echo "DATABASE_PASSWORD=${{ secrets.DATABASE_PASSWORD }}" >> .env diff --git a/.pre-commit b/.pre-commit new file mode 100644 index 0000000..dfe205e --- /dev/null +++ b/.pre-commit @@ -0,0 +1,36 @@ +#!/bin/bash + +# Path to composer.json +COMPOSER_FILE="composer.json" + +# Check if composer.json exists +if [ ! -f "$COMPOSER_FILE" ]; then + echo "$COMPOSER_FILE not found!" + exit 1 +fi + +# Use jq to bump the patch version +if command -v jq &> /dev/null; then + # Read the current version + CURRENT_VERSION=$(jq -r '.version' "$COMPOSER_FILE") + + # Split the version into an array + IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT_VERSION" + + # Increment the patch version + NEW_PATCH=$((PATCH + 1)) + + # Create the new version string + NEW_VERSION="$MAJOR.$MINOR.$NEW_PATCH" + + # Update the composer.json file + jq --arg new_version "$NEW_VERSION" '.version = $new_version' "$COMPOSER_FILE" > tmp.$.json && mv tmp.$.json "$COMPOSER_FILE" + + echo "Bumped version from $CURRENT_VERSION to $NEW_VERSION" + + # Stage the changes to package.json + git add composer.json +else + echo "jq is not installed. Please install jq to use this hook." + exit 1 +fi diff --git a/LICENSE b/LICENSE index 03e63df..25c0ab4 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ BSD 3-Clause License -Copyright (c) 2025, Danny Coulombe +Copyright (c) 2025, JSON.ms Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/README.md b/README.md new file mode 100644 index 0000000..fcef110 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# @jsonms/server + +The server to use with your instance of [jsonms-www](https://github.com/JSON-ms/www). diff --git a/composer.json b/composer.json index 4ad4450..930a198 100644 --- a/composer.json +++ b/composer.json @@ -1,4 +1,11 @@ { + "version": "1.0.8", + "name": "jsonms/server", + "description": "The JSON.ms Request Handler Server is a robust backend solution designed to manage and process all incoming requests from the main JSON.ms website.", + "license": "BSD-3-Clause", + "scripts": { + "setup-hooks": "cp .pre-commit .git/hooks/pre-commit && chmod +x .git/hooks/pre-commit" + }, "require": { "google/apiclient": "^2.18", "vlucas/phpdotenv": "^5.6", @@ -6,6 +13,7 @@ "ext-openssl": "*", "monolog/monolog": "^2.5", "psr/log": "^2.0", - "ext-pdo": "*" + "ext-pdo": "*", + "ext-fileinfo": "*" } -} \ No newline at end of file +} diff --git a/src/controllers/BaseController.php b/src/controllers/BaseController.php index c3d5031..45d6208 100644 --- a/src/controllers/BaseController.php +++ b/src/controllers/BaseController.php @@ -79,14 +79,24 @@ public function query(string $query, array $params = []): PDOStatement { return $this->queryRaw($sql, $params); } - public function getCurrentUser() { - if ($this->user == null) { + public function getCurrentUserId() { + if ($this->user == null && isset($_SESSION['user_id'])) { $stmt = $this->query('get-user-by-id', [ - 'id' => $_SESSION['user']['id'] + 'id' => $_SESSION['user_id'] ]); $this->user = $stmt->fetch(PDO::FETCH_OBJ); + if ($this->user) { + return $this->user->id; + } + return null; } - return $this->user; + return $this->user->id; + } + + protected function getHash($length = 10): string { + $bytes = random_bytes($length); + $result = bin2hex($bytes); + return substr($result, 0, $length); } public function encrypt($data, $encryptionKey) { diff --git a/src/controllers/ErrorController.php b/src/controllers/ErrorController.php new file mode 100644 index 0000000..0ac1cb1 --- /dev/null +++ b/src/controllers/ErrorController.php @@ -0,0 +1,29 @@ +query('insert-error', [ + 'key' => $error->key, + 'message' => $error->message, + 'source' => $error->source, + 'line' => $error->line, + 'column' => $error->column, + 'stack' => $error->stack, + 'occurred_on' => $error->occurred_on, + 'last_timestamp' => $error->last_timestamp, + 'version' => $error->version, + 'route' => $error->route, + 'count' => $error->count, + 'user_agent' => $error->user_agent, + 'created_by' => $this->getCurrentUserId(), + ]); + } + + $this->responseJson(true); + } +} diff --git a/src/controllers/GoogleController.php b/src/controllers/GoogleController.php index 1fbc135..aec526b 100644 --- a/src/controllers/GoogleController.php +++ b/src/controllers/GoogleController.php @@ -30,32 +30,25 @@ public function callbackAction() { if ($stmt->rowCount() > 0) { // User exists, fetch data $user = $stmt->fetch(PDO::FETCH_ASSOC); + $userId = $user['id']; } else { // User does not exist, insert new user $this->query('insert-user', [ 'id' => $userInfo->id, 'name' => $userInfo->name, 'email' => $userInfo->email, - 'picture' => $userInfo->picture + 'avatar' => $userInfo->picture ]); $userId = $this->getLastInsertedId(); // Get the new user's ID - $user = [ - 'id' => $userId, - 'googleId' => $userInfo->id, - 'name' => $userInfo->name, - 'email' => $userInfo->email, - 'avatar' => $userInfo->picture, - ]; } // Store user information in the session - $_SESSION['user'] = $user; + $_SESSION['user_id'] = $userId; $_SESSION['access_token'] = $token['access_token']; // Redirect to a protected page or dashboard $decodedState = json_decode(urldecode($state), true); - setcookie("PHPSESSID", session_id(), time() + 3600 * 24 * 7, "/", $_ENV['INTERFACE_EDITOR_DOMAIN']); header('Location: ' . $_ENV['INTERFACE_EDITOR_URL'] . $decodedState['path']); exit; } else { diff --git a/src/controllers/InterfaceController.php b/src/controllers/InterfaceController.php index d21bf7c..81ad009 100644 --- a/src/controllers/InterfaceController.php +++ b/src/controllers/InterfaceController.php @@ -6,7 +6,7 @@ class InterfaceController extends RestfulController { public function indexAction() { $stmt = $this->query('get-all-interfaces', [ - 'userId' => $this->getCurrentUser()->id, + 'userId' => $this->getCurrentUserId(), ]); $users = $stmt->fetchAll(); $this->responseJson($users); @@ -19,18 +19,14 @@ public function getAction($id) { public function createAction($data) { $hash = $this->getHash(); - $cypherKey = $this->getHash(24); - $serverSecret = $this->encrypt($this->getHash(24), $cypherKey); - $encryptedCypherKey = $this->encrypt($cypherKey, $_ENV['JSONMS_CYPHER_KEY']); + $this->query('insert-interface', [ 'hash' => $hash, 'label' => $data->label, 'logo' => $data->logo, 'content' => $data->content, - 'server_url' => $data->server_url, - 'server_secret' => $serverSecret, - 'cypher_key' => $encryptedCypherKey, - 'created_by' => $this->getCurrentUser()->id, + 'webhook' => $data->webhook, + 'created_by' => $this->getCurrentUserId(), ]); // Get inserted interface @@ -55,12 +51,12 @@ public function updateAction($id, $data) { 'label' => $data->label, 'logo' => $data->logo, 'content' => $data->content, - 'server_url' => $data->server_url, - 'userId' => $this->getCurrentUser()->id, + 'webhook' => $data->webhook, + 'userId' => $this->getCurrentUserId(), ]); // Clear all existing permissions (will be added later on) - if ($data->created_by === $this->getCurrentUser()->id) { + if ($data->created_by === $this->getCurrentUserId()) { $this->updatePermissions($data); } @@ -72,7 +68,7 @@ public function deleteAction($id) { if ($this->hasAccess($id)) { $stmt = $this->query('delete-interface', [ 'uuid' => $id, - 'userId' => $this->getCurrentUser()->id, + 'userId' => $this->getCurrentUserId(), ]); if ($stmt->rowCount() > 0) { $this->responseJson(true); @@ -80,25 +76,6 @@ public function deleteAction($id) { } } - public function secretKeyAction($uuid) { - $interface = $this->getAccessibleInterface($uuid); - $decryptedCypherKey = $this->decrypt($interface->cypher_key, $_ENV['JSONMS_CYPHER_KEY']); - $decryptedServerKey = $this->decrypt($interface->server_secret, $decryptedCypherKey); - $this->responseJson($decryptedServerKey); - } - - public function cypherKeyAction($uuid) { - $interface = $this->getAccessibleInterface($uuid); - $decryptedCypherKey = $this->decrypt($interface->cypher_key, $_ENV['JSONMS_CYPHER_KEY']); - $this->responseJson($decryptedCypherKey); - } - - private function getHash($length = 10): string { - $bytes = random_bytes($length); - $result = bin2hex($bytes); - return substr($result, 0, $length); - } - private function hasAccess($uuid, $showError = true): bool { return (bool) $this->getAccessibleInterface($uuid, $showError); } @@ -106,7 +83,7 @@ private function hasAccess($uuid, $showError = true): bool { private function getAccessibleInterface($uuid, $showError = true): false | stdClass { $stmt = $this->query('get-accessible-interface-by-uuid', [ 'uuid' => $uuid, - 'userId' => $this->getCurrentUser()->id, + 'userId' => $this->getCurrentUserId(), ]); if ($stmt->rowCount() > 0) { $interface = $stmt->fetch(PDO::FETCH_OBJ); @@ -129,7 +106,7 @@ private function copyToHistory(stdClass $interface) { $this->query('insert-history', [ 'uuid' => $interface->uuid, 'content' => $interface->content, - 'userId' => $this->getCurrentUser()->id, + 'userId' => $this->getCurrentUserId(), ]); } diff --git a/src/controllers/SessionController.php b/src/controllers/SessionController.php index e7f4de7..1618233 100644 --- a/src/controllers/SessionController.php +++ b/src/controllers/SessionController.php @@ -4,51 +4,79 @@ class SessionController extends RestfulController { + private function getDemoInterface() { + $stmt = $this->query('get-demo-interface'); + if ($stmt->rowCount() > 0) { + $rows = $stmt->fetchAll(PDO::FETCH_OBJ); + foreach ($rows as $row) { + $row->permission_admin = []; + $row->permission_interface = []; + $row->type = 'interface,admin'; + return $row; + } + } + return null; + } + + private function getWebhooks($userId) { + $stmt = $this->query('get-all-webhooks', [ + 'userId' => $userId, + ]); + if ($stmt->rowCount() > 0) { + return $stmt->fetchAll(PDO::FETCH_OBJ); + } + return []; + } + public function indexAction() { $loggedIn = isset($_SESSION['access_token']) && $_SESSION['access_token']; $user = null; - $interfaces = []; $loginUrl = null; + $interfaces = []; + $webhooks = []; + + // Fetch demo interface + $demo = $this->getDemoInterface(); + if ($demo) { + $interfaces[] = $demo; + } if ($loggedIn) { // Check if user already exists $stmt = $this->query('get-user-by-id', [ - 'id' => $this->getCurrentUser()->id, + 'id' => $this->getCurrentUserId(), ]); if ($stmt->rowCount() > 0) { // User exists, fetch data $user = $stmt->fetch(PDO::FETCH_OBJ); - - // Fetch all interfaces - $stmt = $this->query('get-all-interfaces', [ - 'userId' => $this->getCurrentUser()->id, - ]); - - if ($stmt->rowCount() > 0) { - $rows = $stmt->fetchAll(PDO::FETCH_OBJ); - foreach ($rows as $row) { - $row->permission_admin = array_filter(explode(',', $row->permission_admin ?? '')); - $row->permission_interface = array_filter(explode(',', $row->permission_interface ?? '')); - $interfaces[] = $row; - } - } + } else { + // Google Client Configuration + $client = new Google_Client(); + $client->setClientId($_ENV['GOOGLE_OAUTH_CLIENT_ID']); + $client->setClientSecret($_ENV['GOOGLE_OAUTH_CLIENT_SECRET']); + $client->setRedirectUri($_ENV['GOOGLE_OAUTH_CALLBACK_URL']); + $client->addScope('email'); + $client->addScope('profile'); + $loginUrl = $client->createAuthUrl(); } } else { - - // Google Client Configuration - $client = new Google_Client(); - $client->setClientId($_ENV['GOOGLE_OAUTH_CLIENT_ID']); - $client->setClientSecret($_ENV['GOOGLE_OAUTH_CLIENT_SECRET']); - $client->setRedirectUri($_ENV['GOOGLE_OAUTH_CALLBACK_URL']); - $client->addScope('email'); - $client->addScope('profile'); - - $loginUrl = $client->createAuthUrl(); + try { + // Google Client Configuration + $client = new Google_Client(); + $client->setClientId($_ENV['GOOGLE_OAUTH_CLIENT_ID']); + $client->setClientSecret($_ENV['GOOGLE_OAUTH_CLIENT_SECRET']); + $client->setRedirectUri($_ENV['GOOGLE_OAUTH_CALLBACK_URL']); + $client->addScope('email'); + $client->addScope('profile'); + $loginUrl = $client->createAuthUrl(); + } catch(\Exception $e) { + throwError(500, $e->getMessage()); + } try { $oauth2 = new Google_Service_Oauth2($client); @@ -56,16 +84,18 @@ public function indexAction() { $loggedIn = true; } catch(\Exception $e) { $this->responseJson([ + 'error' => $e->getMessage(), 'loggedIn' => false, 'user' => $user, 'googleOAuthSignInUrl' => $loginUrl, 'interfaces' => $interfaces, + 'webhooks' => $webhooks, ]); } // Check if user already exists $stmt = $this->query('get-user-by-google-id', [ - 'id' => $this->getCurrentUser()->id, + 'id' => $this->getCurrentUserId(), ]); if ($stmt->rowCount() > 0) { @@ -73,11 +103,35 @@ public function indexAction() { } } + if ($loggedIn && isset($user)) { + + // Fetch all interfaces + $stmt = $this->query('get-all-interfaces', [ + 'userId' => $this->getCurrentUserId(), + ]); + $interfaces = []; + if ($stmt->rowCount() > 0) { + $rows = $stmt->fetchAll(PDO::FETCH_OBJ); + foreach ($rows as $row) { + $row->permission_admin = array_filter(explode(',', $row->permission_admin ?? '')); + $row->permission_interface = array_filter(explode(',', $row->permission_interface ?? '')); + $interfaces[] = $row; + } + } + } + + // Fetch demo interface + $loggedIn = $loggedIn && isset($user); + if ($loggedIn) { + $webhooks = $this->getWebhooks($user->id); + } + $this->responseJson([ - 'loggedIn' => $loggedIn && isset($user), + 'loggedIn' => $loggedIn, 'user' => $user, 'googleOAuthSignInUrl' => $loginUrl, 'interfaces' => $interfaces, + 'webhooks' => $webhooks, ]); } @@ -96,11 +150,20 @@ public function logoutAction() { // Generate the login URL $loginUrl = $client->createAuthUrl(); + // Fetch demo interface + $interfaces = []; + $demo = $this->getDemoInterface(); + if ($demo) { + $interfaces[] = $demo; + } + // Return the JSON response $this->responseJson([ 'loggedIn' => false, 'user' => null, - 'googleOAuthSignInUrl' => $loginUrl + 'googleOAuthSignInUrl' => $loginUrl, + 'interfaces' => $interfaces, + 'webhooks' => [], ]); } } diff --git a/src/controllers/WebhookController.php b/src/controllers/WebhookController.php new file mode 100644 index 0000000..ccf201f --- /dev/null +++ b/src/controllers/WebhookController.php @@ -0,0 +1,72 @@ +uuid)) { + $this->query('update-webhook-by-uuid', [ + 'uuid' => $webhook->uuid, + 'url' => $webhook->url, + 'userId' => $this->getCurrentUserId(), + ]); + } else { + $cypherKey = $this->getHash(24); + $serverSecret = $this->encrypt($this->getHash(24), $cypherKey); + $encryptedCypherKey = $this->encrypt($cypherKey, $_ENV['JSONMS_CYPHER_KEY']); + $this->query('insert-webhook', [ + 'url' => $webhook->url, + 'secret' => $serverSecret, + 'cypher' => $encryptedCypherKey, + 'created_by' => $this->getCurrentUserId(), + ]); + } + } + $stmt = $this->query('get-all-webhooks', [ + 'userId' => $this->getCurrentUserId(), + ]); + + if ($stmt->rowCount() > 0) { + $this->responseJson($stmt->fetchAll(PDO::FETCH_OBJ)); + } + } + + public function deleteAction($id) { + $stmt = $this->query('delete-webhook-by-uuid', [ + 'uuid' => $id, + 'userId' => $this->getCurrentUserId(), + ]); + if ($stmt->rowCount() > 0) { + $this->responseJson(true); + } + } + + public function secretKeyAction($uuid) { + $webhook = $this->getWebhook($uuid); + $decryptedCypherKey = $this->decrypt($webhook->cypher, $_ENV['JSONMS_CYPHER_KEY']); + $decryptedServerKey = $this->decrypt($webhook->secret, $decryptedCypherKey); + $this->responseJson($decryptedServerKey); + } + + public function cypherKeyAction($uuid) { + $webhook = $this->getWebhook($uuid); + $decryptedCypherKey = $this->decrypt($webhook->cypher, $_ENV['JSONMS_CYPHER_KEY']); + $this->responseJson($decryptedCypherKey); + } + + private function getWebhook($uuid, $showError = true): false | stdClass { + $stmt = $this->query('get-webhook-by-uuid', [ + 'uuid' => $uuid, + 'userId' => $this->getCurrentUserId(), + ]); + if ($stmt->rowCount() > 0) { + return $stmt->fetch(PDO::FETCH_OBJ); + } + if ($showError) { + throwError(403, 'You don\'t have permission to access this webhook'); + } + return false; + } +} diff --git a/src/queries/delete-webhook-by-uuid.sql b/src/queries/delete-webhook-by-uuid.sql new file mode 100644 index 0000000..2f482b0 --- /dev/null +++ b/src/queries/delete-webhook-by-uuid.sql @@ -0,0 +1,3 @@ +DELETE FROM webhooks +WHERE uuid = :uuid + AND created_by = :userId diff --git a/src/queries/get-all-interfaces.sql b/src/queries/get-all-interfaces.sql index 4e5e9d9..b4d3f8a 100644 --- a/src/queries/get-all-interfaces.sql +++ b/src/queries/get-all-interfaces.sql @@ -3,23 +3,30 @@ SELECT GROUP_CONCAT(DISTINCT(pi.email)) as permission_interface, GROUP_CONCAT(DISTINCT(pa.email)) as permission_admin, 'owner' AS type, - u.name AS owner_name + u.name AS owner_name, + w.url AS server_url, + w.secret AS server_secret FROM interfaces AS i - INNER JOIN users AS u ON u.id = i.created_by - LEFT JOIN permissions AS pa ON pa.interface_uuid = i.uuid AND pa.type = 'admin' - LEFT JOIN permissions AS pi ON pi.interface_uuid = i.uuid AND pi.type = 'interface' + INNER JOIN users AS u ON u.id = i.created_by + LEFT JOIN permissions AS pa ON pa.interface_uuid = i.uuid AND pa.type = 'admin' + LEFT JOIN permissions AS pi ON pi.interface_uuid = i.uuid AND pi.type = 'interface' + LEFT JOIN webhooks AS w ON w.uuid = i.webhook WHERE i.created_by = :userId GROUP BY i.uuid -UNION +UNION ALL SELECT i.*, - null AS permission_interface, - null AS permission_admin, - p.type, - owner.name AS owner_name + null AS permission_interface, + null AS permission_admin, + GROUP_CONCAT(DISTINCT(p.type)) as type, + owner.name AS owner_name, + w.url AS server_url, + w.secret AS server_secret FROM permissions AS p -INNER JOIN users AS u ON u.email = p.email -INNER JOIN interfaces AS i ON i.uuid = p.interface_uuid -INNER JOIN users AS owner ON owner.id = i.created_by + INNER JOIN users AS u ON u.email = p.email + INNER JOIN interfaces AS i ON i.uuid = p.interface_uuid + INNER JOIN users AS owner ON owner.id = i.created_by + LEFT JOIN webhooks AS w ON w.uuid = i.webhook WHERE u.id = :userId +GROUP BY i.uuid diff --git a/src/queries/get-all-webhooks.sql b/src/queries/get-all-webhooks.sql new file mode 100644 index 0000000..ddcd0d5 --- /dev/null +++ b/src/queries/get-all-webhooks.sql @@ -0,0 +1,5 @@ +SELECT + w.* +FROM webhooks AS w +WHERE + w.created_by = :userId diff --git a/src/queries/get-demo-interface.sql b/src/queries/get-demo-interface.sql new file mode 100644 index 0000000..e438369 --- /dev/null +++ b/src/queries/get-demo-interface.sql @@ -0,0 +1,10 @@ +SELECT + i.*, + u.name AS owner_name, + w.url AS server_url, + w.secret AS server_secret +FROM interfaces AS i + INNER JOIN users AS u ON u.id = i.created_by + LEFT JOIN webhooks AS w ON w.uuid = i.webhook +WHERE i.hash = "demo" +GROUP BY i.uuid diff --git a/src/queries/get-interface-by-hash.sql b/src/queries/get-interface-by-hash.sql index ac2f793..04ea819 100644 --- a/src/queries/get-interface-by-hash.sql +++ b/src/queries/get-interface-by-hash.sql @@ -3,11 +3,14 @@ SELECT GROUP_CONCAT(DISTINCT(pi.email)) as permission_interface, GROUP_CONCAT(DISTINCT(pa.email)) as permission_admin, 'owner' AS type, - u.name AS owner_name + u.name AS owner_name, + w.url AS server_url, + w.secret AS server_secret FROM interfaces AS i INNER JOIN users AS u ON u.id = i.created_by LEFT JOIN permissions AS pa ON pa.interface_uuid = i.uuid AND pa.type = 'admin' LEFT JOIN permissions AS pi ON pi.interface_uuid = i.uuid AND pi.type = 'interface' + LEFT JOIN webhooks AS w ON w.uuid = i.webhook WHERE i.hash = :hash GROUP BY - i.uuid, u.name; \ No newline at end of file + i.uuid, u.name; diff --git a/src/queries/get-interface-by-uuid.sql b/src/queries/get-interface-by-uuid.sql index 6305471..107721e 100644 --- a/src/queries/get-interface-by-uuid.sql +++ b/src/queries/get-interface-by-uuid.sql @@ -3,11 +3,14 @@ SELECT GROUP_CONCAT(DISTINCT(pi.email)) as permission_interface, GROUP_CONCAT(DISTINCT(pa.email)) as permission_admin, 'owner' AS type, - u.name AS owner_name + u.name AS owner_name, + w.url AS server_url, + w.secret AS server_secret FROM interfaces AS i INNER JOIN users AS u ON u.id = i.created_by LEFT JOIN permissions AS pa ON pa.interface_uuid = i.uuid AND pa.type = 'admin' LEFT JOIN permissions AS pi ON pi.interface_uuid = i.uuid AND pi.type = 'interface' + LEFT JOIN webhooks AS w ON w.uuid = i.webhook WHERE i.uuid = :uuid GROUP BY - i.uuid, u.name; \ No newline at end of file + i.uuid, u.name; diff --git a/src/queries/get-webhook-by-uuid.sql b/src/queries/get-webhook-by-uuid.sql new file mode 100644 index 0000000..d3fbc01 --- /dev/null +++ b/src/queries/get-webhook-by-uuid.sql @@ -0,0 +1,6 @@ +SELECT + w.* +FROM webhooks AS w +WHERE + w.uuid = :uuid + AND w.created_by = :userId diff --git a/src/queries/insert-error.sql b/src/queries/insert-error.sql new file mode 100644 index 0000000..cbec6db --- /dev/null +++ b/src/queries/insert-error.sql @@ -0,0 +1,3 @@ +INSERT INTO errors (`key`, `message`, `source`, `line`, `column`, `stack`, `occurred_on`, `last_timestamp`, `version`, `route`, `count`, `user_agent`, `created_by`) +VALUES (:key, :message, :source, :line, :column, :stack, :occurred_on, :last_timestamp, :version, :route, :count, :user_agent, :created_by) +ON DUPLICATE KEY UPDATE `count` = `count` + :count, `updated_at` = NOW(); \ No newline at end of file diff --git a/src/queries/insert-interface.sql b/src/queries/insert-interface.sql index 26c344c..be1a49a 100644 --- a/src/queries/insert-interface.sql +++ b/src/queries/insert-interface.sql @@ -1,2 +1,2 @@ -INSERT INTO interfaces (hash, label, logo, content, server_url, server_secret, cypher_key, created_by) -VALUES (:hash, :label, :logo, :content, :server_url, :server_secret, :cypher_key, :created_by) +INSERT INTO interfaces (hash, label, logo, content, webhook, created_by) +VALUES (:hash, :label, :logo, :content, :webhook, :created_by) diff --git a/src/queries/insert-user.sql b/src/queries/insert-user.sql index 1b24152..9b11b8f 100644 --- a/src/queries/insert-user.sql +++ b/src/queries/insert-user.sql @@ -1,2 +1,2 @@ INSERT INTO users (google_id, name, email, avatar) -VALUES (:google_id, :name, :email, :avatar) +VALUES (:id, :name, :email, :avatar) diff --git a/src/queries/insert-webhook.sql b/src/queries/insert-webhook.sql new file mode 100644 index 0000000..677bc26 --- /dev/null +++ b/src/queries/insert-webhook.sql @@ -0,0 +1,2 @@ +INSERT INTO webhooks (url, secret, cypher, created_by) +VALUES (:url, :secret, :cypher, :created_by) diff --git a/src/queries/update-interface-by-uuid.sql b/src/queries/update-interface-by-uuid.sql index 0f4b830..7825d99 100644 --- a/src/queries/update-interface-by-uuid.sql +++ b/src/queries/update-interface-by-uuid.sql @@ -1,3 +1,3 @@ UPDATE interfaces AS i -SET i.label = :label, i.logo = :logo, i.content = :content, i.server_url = :server_url, i.updated_at = NOW() +SET i.label = :label, i.logo = :logo, i.content = :content, i.webhook = :webhook, i.updated_at = NOW() WHERE i.uuid = :uuid diff --git a/src/queries/update-webhook-by-uuid.sql b/src/queries/update-webhook-by-uuid.sql new file mode 100644 index 0000000..a8630b4 --- /dev/null +++ b/src/queries/update-webhook-by-uuid.sql @@ -0,0 +1,4 @@ +UPDATE webhooks AS w +SET w.url = :url, w.updated_at = NOW() +WHERE w.uuid = :uuid + AND w.created_by = :userId diff --git a/utils.php b/utils.php index 6ecfcb3..79685f5 100644 --- a/utils.php +++ b/utils.php @@ -1,7 +1,7 @@