Skip to content

Commit

Permalink
Added server side code. Api and Database
Browse files Browse the repository at this point in the history
  • Loading branch information
CansyLand committed Oct 5, 2023
1 parent 440787d commit 66ae6ca
Show file tree
Hide file tree
Showing 10 changed files with 245 additions and 11 deletions.
1 change: 1 addition & 0 deletions Pixel-Canvas-API/.dclignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ README.md
*.zip
*.rar
src
server
Binary file modified Pixel-Canvas-API/images/scene-thumbnail.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
55 changes: 55 additions & 0 deletions Pixel-Canvas-API/server/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Pixel API Project

This project provides a REST API for managing pixel data. The API allows you to retrieve, add, and update pixel information in a database. The provided examples utilize restdb.io and SQLite databases. Restdb.io offers a straightforward setup, while SQLite offers more control and portability.

## Getting Started

These instructions will guide you through setting up the project on your local machine for development and testing purposes.

### Prerequisites

- Basic knowledge of REST APIs.
- A code editor like Visual Studio Code.
- [Postman](https://www.postman.com/) for testing the API.

### Database Setup

#### Option 1: Restdb.io

1. Create an account on [restdb.io](https://restdb.io/).
2. Follow the instructions to set up a new database.
3. Restdb.io will automatically generate the API code for you.

> **Note:** The free version of restdb.io has a rate limit of 1 request per second.
#### Option 2: SQLite with PHP

1. Ensure your server has PHP installed.
2. Copy all files from the `/php-sqlite` directory onto your server.
3. Ensure the `.htaccess` file is included as it formats the URL into arguments for the script.

> **Note:** With SQLite, you get a lightweight compact database in a single file that you can copy and move around. You can inspect the `.sqlite` database using [SQLite Viewer](https://sqliteviewer.app/).
### API Usage

- **Get Pixels:** Send a GET request to the `/pixels` endpoint to retrieve all pixel data.
- **Add Pixels:** Send a POST request to the `/pixels` endpoint with pixel data in the request body to add new pixels.
- **Update Pixel:** Send a PUT request to the `/pixels/{id}` endpoint with updated pixel data in the request body to update an existing pixel.

### Running the API Locally

- For the restdb.io setup, no further action is needed as it's hosted on restdb.io.
- For the SQLite setup, upload the files to a PHP-enabled server, and access the API via the server's URL.

### Additional Resources

- [Restdb.io Documentation](https://restdb.io/docs/)
- [SQLite Documentation](https://www.sqlite.org/docs.html)

### Contributing

Feel free to submit issues or pull requests to improve the project.

### License

This project is open-source. See the LICENSE file for details.
Binary file not shown.
5 changes: 5 additions & 0 deletions Pixel-Canvas-API/server/php-sqlite/.htaccess
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([^/]+)/?$ rest-api.php?file=$1 [QSA,L]
RewriteRule ^([^/]+)/([^/]+)/?$ rest-api.php?file=$1&id=$2 [QSA,L]
177 changes: 177 additions & 0 deletions Pixel-Canvas-API/server/php-sqlite/rest-api.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
<?php
header('Content-Type: application/json');
// Replace `*` with `https://decentraland.org` to restrict access
header('Access-Control-Allow-Origin: *');

// It's a preflight request. Respond accordingly.
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
header('Access-Control-Allow-Methods: GET, POST, PUT');
header('Access-Control-Allow-Headers: Content-Type, x-apikey, cache-control');
exit;
}


// PARAMETER

// This is your API token
// $apiToken = "COME_UP_WITH_YOUR_OWN_API_TOKEN_HERE";
$apiToken = "6518855968885408010c01fc";

// Here you can define your table
function ensureTableExists($db, $table) {
global $table; // Access the global $table variable
$db->exec("CREATE TABLE IF NOT EXISTS $table (
_id INTEGER PRIMARY KEY AUTOINCREMENT,
posX INTEGER,
posY INTEGER,
hexColor TEXT
)");
}




// Check for valid API token
if ($_SERVER['HTTP_X_APIKEY'] !== $apiToken) {
http_response_code(401); // Unauthorized
echo json_encode(['error' => 'Invalid API token']);
exit;
}

// Function to sanitize file or table names
function sanitizeName($name) {
$name = preg_replace("/[^a-zA-Z0-9]/", '', $name); // Remove non-alphanumeric characters
$name = substr($name, 0, 50); // Limit the length to 50 characters
return $name;
}

// Get and sanitize the 'table' parameter
$table = isset($_GET['table']) ? sanitizeName($_GET['table']) : 'pixels';

// Get and sanitize the 'id' parameter
$id = isset($_GET['id']) ? sanitizeName($_GET['id']) : null;

// Set up the database connection
try {
$db = new PDO('sqlite: database.sqlite');
} catch (PDOException $e) {
http_response_code(500);
echo json_encode(['error' => 'Database connection failed: ' . $e->getMessage()]);
exit;
}

// Handle different request methods
switch ($_SERVER['REQUEST_METHOD']) {
case 'GET':
getPixels($db, $table);
break;
case 'POST':
initPixels($db, $table);
break;
case 'PUT':
updatePixel($db, $table, $id);
break;
default:
echo json_encode(['message' => 'Method not supported']);
break;
}

// Returns all pixel in the table, if table does not exist returns ´[]´
function getPixels($db, $table) {
try {
$query = "SELECT * FROM $table";
$stmt = $db->prepare($query);
$stmt->execute();
$pixels = $stmt->fetchAll(PDO::FETCH_ASSOC);
echo json_encode($pixels);
} catch (Exception $e) {
http_response_code(200);
echo json_encode([]);
}
}

// Takes array of pixel data and inits the sqlLite database
// Then returns the array with _id
function initPixels($db, $table) {
ensureTableExists($db, $table);

$inputData = json_decode(file_get_contents('php://input'), true);

if ($inputData === null || !is_array($inputData)) {
http_response_code(400);
echo json_encode(['error' => 'Invalid JSON data provided']);
return;
}

$successfulEntries = []; // Array to collect successfully added entries
$errors = []; // Array to collect error messages

foreach ($inputData as $entry) {
if (!isset($entry['posX'], $entry['posY'], $entry['hexColor'])) {
$errors[] = 'Missing required fields for a pixel entry.';
continue; // Skip to next entry if validation fails
}

try {
$stmt = $db->prepare("INSERT INTO $table (posX, posY, hexColor) VALUES (?, ?, ?)");
$stmt->execute([$entry['posX'], $entry['posY'], $entry['hexColor']]);
$insertId = $db->lastInsertId();
$entry['_id'] = intval($insertId); // Add the _id to the entry
$successfulEntries[] = $entry; // Store the entry with _id
} catch (Exception $e) {
$errors[] = $e->getMessage(); // Collect error messages
}
}

// Check for errors
if (!empty($errors)) {
http_response_code(400); // Bad Request
echo json_encode(['errors' => $errors]); // Return the error messages
} else {
echo json_encode($successfulEntries); // Return the successfully added entries
}
}

function updatePixel($db, $table, $id) {

// Ensure the table exists before attempting to update data
ensureTableExists($db, $table);

// Get input data
$inputData = json_decode(file_get_contents('php://input'), true);

// Check if inputData is not null and is an array
if ($inputData === null || empty($id)) {
http_response_code(400);
echo json_encode(['error' => 'No JSON data provided or missing id']);
return;
}

// Validate input data
if (!isset($inputData['posX'], $inputData['posY'], $inputData['hexColor'])) {
http_response_code(400);
echo json_encode(['error' => 'Missing required fields']);
return;
}

try {
$query = "UPDATE $table SET posX = ?, posY = ?, hexColor = ? WHERE _id = ?";
$stmt = $db->prepare($query);
$stmt->execute([$inputData['posX'], $inputData['posY'], $inputData['hexColor'], $id]);

// Check if any row was updated
if ($stmt->rowCount() > 0) {
$updatedData = array_merge(['_id' => $id], $inputData); // Combine _id with input data
echo json_encode($updatedData); // Return the updated data
} else {
http_response_code(404);
echo json_encode(['error' => 'No pixel found with _id ' . $id]);
}
} catch (Exception $e) {
http_response_code(500);
echo json_encode(['error' => $e->getMessage()]);
}
}


?>
6 changes: 3 additions & 3 deletions Pixel-Canvas-API/src/api.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// API key here
const apiKey = '6518855968885408010c01fc'
const urlRestAPI = 'https://goerliplasapixelcanv-9b7d.restdb.io/rest/pixel'
const urlRestAPI = 'https://goerliplasapixelcanv-9b7d.restdb.io/rest/pixels'

// Gets all pixel from database via GET API call
export function getDatabase(): Promise<any> {
Expand Down Expand Up @@ -37,7 +37,7 @@ export function initDatabase(canvasWidth: number, canvasHeight: number, color: s
const pixelData = {
posX: x,
posY: y,
color: color
hexColor: color
}
pixels.push(pixelData)
}
Expand Down Expand Up @@ -70,7 +70,7 @@ export function updateDatabase(id: string, posX: number, posY: number, color: st
const data = {
posX: posX,
posY: posY,
color: color
hexColor: color
}

// PUT overrides the pixel in database selected by id
Expand Down
2 changes: 1 addition & 1 deletion Pixel-Canvas-API/src/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export function createPixel(canvas: Entity, posX: number, posY: number, hexColor
// Write pixel color into database via API
executeTask(async () => {
const updatedPixel = await updateDatabase(id, pos.x, pos.y, playersColor)
console.log(updatedPixel)
console.log('ApiResponse:', updatedPixel)
if (!updatedPixel) return

// Send new pixel data to all players in scene
Expand Down
7 changes: 2 additions & 5 deletions Pixel-Canvas-API/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
// color pallette
// Screenshot!

import { Entity, engine } from '@dcl/sdk/ecs'
import { getDatabase, initDatabase } from './api'
import { Quaternion } from '@dcl/sdk/math'
Expand Down Expand Up @@ -54,8 +51,8 @@ export async function main() {
console.log(pixelData)

// Fill canvas with pixel from database
pixelData.forEach((pixel: { posX: number; posY: number; color: string; _id: string }) => {
createPixel(canvas, pixel.posX, pixel.posY, pixel.color, pixel._id)
pixelData.forEach((pixel: { posX: number; posY: number; hexColor: string; _id: string }) => {
createPixel(canvas, pixel.posX, pixel.posY, pixel.hexColor, pixel._id)
})

// Create color picker
Expand Down
3 changes: 1 addition & 2 deletions Pixel-Canvas-API/src/messageBus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ export const sceneMessageBus = new MessageBus()
// So everyone can see the most recent pixels,
// without calling API all the time
sceneMessageBus.on('updatePixelColor', (newPixel) => {
console.log('MessageBus:')
console.log(newPixel)
console.log('MessageBus :', newPixel)
// Get all entities with custom component Pixel
const pixels = engine.getEntitiesWith(Pixel)
for (const [entity] of pixels) {
Expand Down

0 comments on commit 66ae6ca

Please sign in to comment.