Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
3ed0523
no need for setcookie in google callback
dcoulombe-yolarx Mar 13, 2025
999d472
include .env.example
dcoulombe-yolarx Mar 13, 2025
84a2932
fix google callback create user query
dcoulombe-yolarx Mar 13, 2025
9b4ea06
setup github workflows
dannycoulombe Mar 17, 2025
59ee2cf
setup github workflows
dannycoulombe Mar 17, 2025
289cdbd
setup github workflows
dannycoulombe Mar 17, 2025
86cff6a
setup github workflows
dannycoulombe Mar 17, 2025
ea3897d
setup github workflows
dannycoulombe Mar 17, 2025
37cc166
setup github workflows
dannycoulombe Mar 17, 2025
1287ad4
setup github workflows
dannycoulombe Mar 17, 2025
723f837
fix google oauth error
dannycoulombe Mar 17, 2025
bf94859
fix problem with obtaining user id when user failed logging process
dcoulombe-yolarx Mar 26, 2025
ae7aedf
new interface progresss
dcoulombe-yolarx Apr 1, 2025
e8628eb
fix read file cors issue
dcoulombe-yolarx Apr 2, 2025
988176a
fix permissions
dcoulombe-yolarx Apr 3, 2025
ec25167
version bump
dcoulombe-yolarx Apr 3, 2025
cb8336e
readme update
dannycoulombe Apr 3, 2025
2dec1f1
return interface type
dannycoulombe Apr 3, 2025
c70e5ba
new webhooks table support
dcoulombe-yolarx Apr 6, 2025
b012780
new setup-hooks scripts for composer.json
dcoulombe-yolarx Apr 6, 2025
0ddde9c
version bump
dcoulombe-yolarx Apr 6, 2025
dd42102
version bump
dcoulombe-yolarx Apr 6, 2025
8076118
version bump
dcoulombe-yolarx Apr 6, 2025
3a13ce9
fix webhook list null when logging out
dcoulombe-yolarx Apr 7, 2025
65e8acb
handle frontend error logging
dcoulombe-yolarx Apr 7, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
@@ -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
Expand Down
56 changes: 56 additions & 0 deletions .github/workflows/deploy-dev.yml
Original file line number Diff line number Diff line change
@@ -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
56 changes: 56 additions & 0 deletions .github/workflows/deploy-prod.yml
Original file line number Diff line number Diff line change
@@ -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
36 changes: 36 additions & 0 deletions .pre-commit
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -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:
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# @jsonms/server

The server to use with your instance of [jsonms-www](https://github.com/JSON-ms/www).
12 changes: 10 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
{
"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",
"ext-curl": "*",
"ext-openssl": "*",
"monolog/monolog": "^2.5",
"psr/log": "^2.0",
"ext-pdo": "*"
"ext-pdo": "*",
"ext-fileinfo": "*"
}
}
}
18 changes: 14 additions & 4 deletions src/controllers/BaseController.php
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
29 changes: 29 additions & 0 deletions src/controllers/ErrorController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

use JSONms\Controllers\RestfulController;

class ErrorController extends RestfulController
{
public function saveAction($errors)
{
foreach ($errors as $error) {
$this->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);
}
}
13 changes: 3 additions & 10 deletions src/controllers/GoogleController.php
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
43 changes: 10 additions & 33 deletions src/controllers/InterfaceController.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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
Expand All @@ -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);
}

Expand All @@ -72,41 +68,22 @@ 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);
}
}
}

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);
}

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);
Expand All @@ -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(),
]);
}

Expand Down
Loading