Implemented a simple server-side api to store data
This commit is contained in:
parent
f452708aab
commit
4853810a54
3 changed files with 183 additions and 5 deletions
92
api.php
Normal file
92
api.php
Normal file
|
@ -0,0 +1,92 @@
|
|||
<?php
|
||||
/*
|
||||
* api.php handels data storage
|
||||
* Used by Ember Data RESTAdapter to read and write data on server side
|
||||
*
|
||||
* Returns data back to the client on a GET request on ?/poll/:id or ?/user/:id.
|
||||
* Save new data on a POST request on ?/polls or ?/users and return new id back.
|
||||
*
|
||||
* There is no need to check for permission because all data stored on server
|
||||
* is encrypted and could only be read with the correct encryption key. There is
|
||||
* no update oder delete feature yet.
|
||||
*/
|
||||
|
||||
// load classes
|
||||
require_once 'classes/datahandler.php';
|
||||
|
||||
// initialize
|
||||
$datahandler = new datahandler();
|
||||
|
||||
switch ($_SERVER['REQUEST_METHOD']) {
|
||||
// read data
|
||||
case 'GET':
|
||||
// get requested id from uri
|
||||
$query_paramter = split("/",$_SERVER["QUERY_STRING"]);
|
||||
if (isset($query_paramter[2])) {
|
||||
$requested_id = $query_paramter[2];
|
||||
}
|
||||
else {
|
||||
throw new Exception("Requested data but there is no ID in URI");
|
||||
}
|
||||
|
||||
// read data
|
||||
$data = $datahandler->get($requested_id);
|
||||
if ($data === false) {
|
||||
// there was no data with this id or it could not be readen
|
||||
|
||||
// set http header
|
||||
header("HTTP/1.0 404 Not Found");
|
||||
}
|
||||
else {
|
||||
// set http header
|
||||
header("HTTP/1.0 200 OK");
|
||||
|
||||
// forbidde browser to lead javascript from an external location
|
||||
header("Content-Security-Policy: script-src 'self'");
|
||||
|
||||
// set content-type and charset
|
||||
header('Content-Type: application/x-json-encrypted; charset=utf-8');
|
||||
|
||||
// send data back
|
||||
echo $data;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
// write data
|
||||
case 'POST':
|
||||
// get data send with request
|
||||
$data = file_get_contents('php://input');
|
||||
|
||||
$newId = $datahandler->write($data);
|
||||
if ($newId === false) {
|
||||
header("HTTP/1.0 500 Internal Server Error");
|
||||
}
|
||||
else {
|
||||
// set http header
|
||||
header("HTTP/1.0 200 OK");
|
||||
|
||||
// forbidde browser to lead javascript from an external location
|
||||
header("Content-Security-Policy: script-src 'self'");
|
||||
|
||||
// set content-type and charset
|
||||
header('Content-Type: application/x-json-encrypted; charset=utf-8');
|
||||
|
||||
// extend given data with newId
|
||||
$newData = json_decode($data);
|
||||
$newData->poll->id = $newId;
|
||||
|
||||
// send back data
|
||||
echo json_encode($newData);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
// request method is not supported
|
||||
default:
|
||||
// set http header
|
||||
header("HTTP/1.0 400 Bad Request");
|
||||
|
||||
break;
|
||||
}
|
||||
?>
|
80
classes/datahandler.php
Normal file
80
classes/datahandler.php
Normal file
|
@ -0,0 +1,80 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* handles the data for api.php
|
||||
*/
|
||||
class datahandler {
|
||||
// (string) folder to store data in relative to position of api.php
|
||||
// webserver has to have write access to this folder
|
||||
// must end with a slash
|
||||
const DATA_FOLDER = 'data/';
|
||||
|
||||
// (int) length of ids used to identify data
|
||||
const ID_LENGTH = 10;
|
||||
|
||||
// (string) characters used to generate a new id
|
||||
const ID_CHARACTERS = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||
|
||||
/*
|
||||
* read data with $id
|
||||
* return false, if there is no data with this id or if data could not be read
|
||||
*/
|
||||
public function get($id) {
|
||||
// file with absolut path
|
||||
$file = self::DATA_FOLDER . $id;
|
||||
|
||||
// check if file exists and is readable
|
||||
if (!is_readable($file)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return file_get_contents($file);
|
||||
}
|
||||
|
||||
/*
|
||||
* generates a new Id
|
||||
*/
|
||||
protected function generateNewId()
|
||||
{
|
||||
// generate random string
|
||||
$randomString = '';
|
||||
for ($i = 0; $i < self::ID_LENGTH; $i++) {
|
||||
$characters = self::ID_CHARACTERS;
|
||||
$randomString .= $characters[rand(0, strlen($characters) - 1)];
|
||||
}
|
||||
|
||||
// check if id is already used, generate new one if necessary
|
||||
if (file_exists(self::DATA_FOLDER.$randomString)) {
|
||||
$randomString = $this->generateNewId();
|
||||
}
|
||||
|
||||
return $randomString;
|
||||
}
|
||||
|
||||
/*
|
||||
* stores data
|
||||
* returns new id or false on error
|
||||
*/
|
||||
public function write ($data) {
|
||||
// get a new id
|
||||
$new_id = $this->generateNewId();
|
||||
|
||||
$new_file = self::DATA_FOLDER . $new_id;
|
||||
|
||||
// check if new file could be created
|
||||
if (!is_writeable(self::DATA_FOLDER)) {
|
||||
echo "not writeable";
|
||||
return false;
|
||||
}
|
||||
|
||||
// write data
|
||||
if (file_put_contents($new_file, $data, LOCK_EX) === false) {
|
||||
echo "error on write";
|
||||
return false;
|
||||
}
|
||||
|
||||
return $new_id;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
16
index.html
16
index.html
|
@ -108,6 +108,8 @@
|
|||
<script src="lib/handlebars-v1.2.0.js"></script>
|
||||
<script src="lib/ember.js"></script>
|
||||
<script src="lib/ember-data.js"></script>
|
||||
<script src="lib/activemodel-adapter.js"></script>
|
||||
<script src="lib/embedded-records-mixin.js"></script>
|
||||
<script src="lib/localstorage_adapter.js"></script>
|
||||
|
||||
<!-- EMBER APP CODE -->
|
||||
|
@ -118,7 +120,10 @@
|
|||
});
|
||||
|
||||
// adapter initialization
|
||||
App.ApplicationAdapter = DS.LSAdapter;
|
||||
App.ApplicationAdapter = DS.RESTAdapter.extend({
|
||||
// set namespace to api.php in same subdirectory
|
||||
namespace: 'api.php?'
|
||||
});
|
||||
|
||||
/*
|
||||
* models
|
||||
|
@ -137,6 +142,7 @@
|
|||
// option model
|
||||
// used by poll model
|
||||
App.Option = DS.Model.extend({
|
||||
poll : DS.belongsTo('poll', {async: true}),
|
||||
title : DS.attr('string')
|
||||
});
|
||||
|
||||
|
@ -283,10 +289,10 @@
|
|||
actions: {
|
||||
save: function(){
|
||||
// save poll
|
||||
this.get('model').save();
|
||||
|
||||
// redirect to new poll
|
||||
this.transitionToRoute('poll', this.get('model'));
|
||||
this.get('model').save().then(function(){
|
||||
// redirect to new poll
|
||||
this.transitionToRoute('poll', this.get('model'));
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue