зеркало из https://github.com/mozilla/galaxy-api.git
THIS IS GALAXY-API
This commit is contained in:
Коммит
0b840e64bf
|
@ -0,0 +1,17 @@
|
|||
lib-cov
|
||||
*.seed
|
||||
*.log
|
||||
*.csv
|
||||
*.dat
|
||||
*.out
|
||||
*.pid
|
||||
*.gz
|
||||
|
||||
pids
|
||||
logs
|
||||
results
|
||||
build
|
||||
|
||||
node_modules
|
||||
|
||||
settings_local.js
|
|
@ -0,0 +1,32 @@
|
|||
# galaxy-api
|
||||
|
||||
Here lives the API that is consumed by the front-end interface for
|
||||
[cvan/galaxy](cvan/galaxy).
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
* `npm install`
|
||||
* `cp settings_local.js.dist settings_local.js`
|
||||
* `nodemon app.js`
|
||||
|
||||
|
||||
## Sample Usage
|
||||
|
||||
### Game Submission
|
||||
|
||||
curl -X POST 'http://localhost:5000/game/submit' -d 'name=Mario Bros&app_url=http://mariobro.se&icons=128&screenshots=yes'
|
||||
|
||||
### Game Details
|
||||
|
||||
curl 'http://localhost:5000/game/mario-bros/detail'
|
||||
|
||||
### Webapp
|
||||
|
||||
#### Manifest JSON (Firefox)
|
||||
|
||||
curl 'http://localhost:5000/game/mario-bros/manifest'
|
||||
|
||||
#### Manifest Launcher
|
||||
|
||||
curl 'http://localhost:5000/launch.html?https://mariobro.se'
|
|
@ -0,0 +1,17 @@
|
|||
var settings = require('./settings_local.js');
|
||||
|
||||
var db = require('./db');
|
||||
var server = require('./server');
|
||||
|
||||
|
||||
[
|
||||
'game/detail',
|
||||
'game/manifest',
|
||||
'game/submit'
|
||||
].forEach(function(view) {
|
||||
require('./views/' + view)(server);
|
||||
});
|
||||
|
||||
server.listen(process.env.PORT || 5000, function() {
|
||||
console.log('%s listening at %s', server.name, server.url);
|
||||
});
|
|
@ -0,0 +1 @@
|
|||
{"slug":"mario-bros","app_url":"http://mario.com","name":"Mario Bros","icons":"128","screenshots":"yes"}
|
|
@ -0,0 +1,56 @@
|
|||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var url = require('url');
|
||||
|
||||
var redis = require('redis');
|
||||
|
||||
var redisURL = url.parse(process.env.REDIS_URL ||
|
||||
process.env.REDISCLOUD_URL ||
|
||||
process.env.REDISTOGO_URL ||
|
||||
'');
|
||||
redisURL.hostname = redisURL.hostname || 'localhost';
|
||||
redisURL.port = redisURL.port || 6379;
|
||||
var redisClient = redis.createClient(redisURL.port, redisURL.hostname);
|
||||
if (redisURL.auth) {
|
||||
redisClient.auth(redisURL.auth.split(':')[1]);
|
||||
}
|
||||
|
||||
var utils = require('./utils');
|
||||
|
||||
|
||||
function flatDB() {
|
||||
}
|
||||
flatDB.prototype = {
|
||||
write: function(type, slug, data) {
|
||||
if (typeof data !== 'string') {
|
||||
data = JSON.stringify(data);
|
||||
}
|
||||
|
||||
var baseDir = path.resolve('data', type);
|
||||
var fn = path.resolve(baseDir, utils.slugify(slug) + '.json');
|
||||
|
||||
if (!fs.existsSync(baseDir)) {
|
||||
console.error('Directory "' + baseDir + '" does not exist');
|
||||
utils.mkdirRecursive(baseDir);
|
||||
}
|
||||
|
||||
fs.writeFile(fn, data, 'utf8', function(err) {
|
||||
if (err) {
|
||||
console.error('Error creating', fn + ':', err);
|
||||
}
|
||||
});
|
||||
},
|
||||
read: function(type, slug) {
|
||||
var fn = path.resolve('data', type, utils.slugify(slug) + '.json');
|
||||
try {
|
||||
return JSON.parse(fs.readFileSync(fn, 'utf8') || '{}');
|
||||
} catch(e) {
|
||||
console.error('Error reading', fn + ':', e);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
};
|
||||
var flatDBClient = new flatDB();
|
||||
|
||||
module.exports.redis = redisClient;
|
||||
module.exports.flatfile = flatDBClient;
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"name": "galaxy-api",
|
||||
"version": "0.0.1",
|
||||
"dependencies": {
|
||||
"bookshelf": "*",
|
||||
"node-restify-swagger": "*",
|
||||
"node-restify-validation": "*",
|
||||
"redis": "*",
|
||||
"restify": "*"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.8.x",
|
||||
"npm": ">=1.1.x"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/cvan/galaxy-api.git"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
var restify = require('restify');
|
||||
var restifySwagger = require('node-restify-swagger');
|
||||
var restifyValidation = require('node-restify-validation');
|
||||
|
||||
var server = restify.createServer({
|
||||
name: 'galaxy',
|
||||
version: '0.0.1'
|
||||
});
|
||||
|
||||
server.use(restify.acceptParser(server.acceptable));
|
||||
server.use(restify.bodyParser());
|
||||
server.use(restify.gzipResponse());
|
||||
server.use(restify.queryParser());
|
||||
server.use(restifyValidation.validationPlugin({errorsAsArray: false}));
|
||||
|
||||
restifySwagger.configure(server);
|
||||
restifySwagger.loadRestifyRoutes();
|
||||
|
||||
module.exports = server;
|
Двоичный файл не отображается.
|
@ -0,0 +1,77 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
body { margin: 15%; text-align: center; }
|
||||
button { background: green; border: 1px solid #000; color: #fff; cursor: pointer; font-size: 200%; padding: 15px 60px; text-transform: uppercase; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<button>Play</button>
|
||||
<script>
|
||||
(function() {
|
||||
var apps = {};
|
||||
window.apps = apps;
|
||||
|
||||
var mozApps = 'mozApps' in navigator;
|
||||
var chromeApps = 'chrome' in window;
|
||||
|
||||
var manifest = 'http://cvan.github.io/HexGL/';
|
||||
|
||||
if (mozApps) {
|
||||
manifest = 'http://localhost:5000/app/hexgl/manifest/firefox';
|
||||
function getInstalled() {
|
||||
// Don't getInstalled if the page isn't visible.
|
||||
if (document.hidden) {
|
||||
return;
|
||||
}
|
||||
// Get list of installed apps and mark as such.
|
||||
var r = navigator.mozApps.getInstalled();
|
||||
r.onsuccess = function() {
|
||||
r.result.forEach(function(val) {
|
||||
apps[val.manifestURL.split('?')[0]] = val;
|
||||
});
|
||||
};
|
||||
}
|
||||
getInstalled();
|
||||
} else if (chromeApps) {
|
||||
//manifest = 'http://localhost:5000/app/hexgl/manifest/chrome.crx';
|
||||
//manifest = 'http://localhost:5000/static/hexgl-crx.crx';
|
||||
}
|
||||
|
||||
var button = document.querySelector('button');
|
||||
button.addEventListener('click', function() {
|
||||
if (mozApps) {
|
||||
if (manifest in apps) {
|
||||
apps[manifest].launch();
|
||||
return;
|
||||
}
|
||||
var request = navigator.mozApps.install(manifest);
|
||||
request.onsuccess = function() {
|
||||
console.error('Install onsuccess', manifest);
|
||||
var app = this.result;
|
||||
var status;
|
||||
var isInstalled = setInterval(function() {
|
||||
status = request.result.installState;
|
||||
if (status == 'installed') {
|
||||
console.log('Install succeded', manifest);
|
||||
button.blur();
|
||||
apps[manifest] = app;
|
||||
app.launch();
|
||||
clearInterval(isInstalled);
|
||||
}
|
||||
}, 250);
|
||||
};
|
||||
request.onerror = function() {
|
||||
console.error('Install failed:', this.error.name);
|
||||
};
|
||||
} else if (chromeApps) {
|
||||
button.blur();
|
||||
//window.location = manifest;
|
||||
window.open(manifest);
|
||||
}
|
||||
}, false);
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1 @@
|
|||
<script>window.location = window.location.search.substr(1);</script>
|
|
@ -0,0 +1,26 @@
|
|||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
|
||||
|
||||
function mkdirRecursive(dir) {
|
||||
var parent = path.resolve(dir, '../');
|
||||
if (!fs.existsSync(parent)) {
|
||||
mkdirRecursive(parent);
|
||||
}
|
||||
fs.mkdirSync(dir);
|
||||
}
|
||||
|
||||
function slugify(text) {
|
||||
if (typeof text !== 'string') {
|
||||
return text;
|
||||
}
|
||||
return text.toString().toLowerCase()
|
||||
.replace(/\s+/g, '-') // Replace spaces with -
|
||||
.replace(/[^\w\-]+/g, '') // Remove all non-word chars
|
||||
.replace(/\-\-+/g, '-') // Replace multiple - with single -
|
||||
.replace(/^-+/, '') // Trim - from start of text
|
||||
.replace(/-+$/, ''); // Trim - from end of text
|
||||
}
|
||||
|
||||
module.exports.mkdirRecursive = mkdirRecursive;
|
||||
module.exports.slugify = slugify;
|
|
@ -0,0 +1,64 @@
|
|||
var db = require('../.././db');
|
||||
|
||||
|
||||
module.exports = function(server) {
|
||||
// Sample usage:
|
||||
// % curl 'http://localhost:5000/game/mario-bros/detail'
|
||||
server.get({
|
||||
url: '/game/:slug/detail',
|
||||
swagger: {
|
||||
nickname: 'detail',
|
||||
notes: 'Specific details and metadata about a game',
|
||||
summary: 'Game Details'
|
||||
}
|
||||
}, function(req, res) {
|
||||
var GET = req.params;
|
||||
var slug = GET.slug;
|
||||
|
||||
var game = db.flatfile.read('game', slug);
|
||||
|
||||
var keys = [
|
||||
'app_url',
|
||||
'appcache_path',
|
||||
'default_locale',
|
||||
'description',
|
||||
'developer_name',
|
||||
'developer_url',
|
||||
'fullscreen',
|
||||
'homepage_url',
|
||||
'icons',
|
||||
'license',
|
||||
'locales',
|
||||
'name',
|
||||
'orientation',
|
||||
'privacy',
|
||||
'screenshots',
|
||||
'slug'
|
||||
];
|
||||
|
||||
var data = {};
|
||||
|
||||
keys.forEach(function(v) {
|
||||
var item = game[v];
|
||||
if (typeof v === 'object') {
|
||||
if (v.hasOwnProperty('length') && item.length) {
|
||||
data[v] = item;
|
||||
} else if (Object.keys(game[v]).length) {
|
||||
data[v] = item;
|
||||
}
|
||||
} else {
|
||||
data[v] = item;
|
||||
}
|
||||
});
|
||||
|
||||
res.json(data);
|
||||
});
|
||||
|
||||
// TODO: Serve each manifest from a separate subdomain.
|
||||
server.get({
|
||||
url: '/manifest.html'
|
||||
}, function(req, res) {
|
||||
var app_url = req.url.split('?')[1];
|
||||
res.send("<script>window.location = '" + app_url + "';</script>");
|
||||
});
|
||||
};
|
|
@ -0,0 +1,64 @@
|
|||
var db = require('../.././db');
|
||||
|
||||
|
||||
module.exports = function(server) {
|
||||
// Sample usage:
|
||||
// % curl 'http://localhost:5000/game/mario-bros/manifest'
|
||||
server.get({
|
||||
url: '/game/:slug/manifest/firefox',
|
||||
swagger: {
|
||||
nickname: 'manifest',
|
||||
notes: 'Firefox Webapp Manifest JSON synthesised on the fly from app data',
|
||||
summary: 'Webapp manifest'
|
||||
}
|
||||
}, function(req, res) {
|
||||
var GET = req.params;
|
||||
var slug = GET.slug;
|
||||
|
||||
var game = db.flatfile.read('game', slug);
|
||||
|
||||
var keys = [
|
||||
'appcache_path',
|
||||
'default_locale',
|
||||
'description',
|
||||
'icons',
|
||||
'locales',
|
||||
'name',
|
||||
'orientation'
|
||||
];
|
||||
|
||||
var data = {};
|
||||
|
||||
keys.forEach(function(v) {
|
||||
var item = game[v];
|
||||
if (typeof v === 'object') {
|
||||
if (v.hasOwnProperty('length') && item.length) {
|
||||
data[v] = item;
|
||||
} else if (Object.keys(game[v]).length) {
|
||||
data[v] = item;
|
||||
}
|
||||
} else {
|
||||
data[v] = item;
|
||||
}
|
||||
});
|
||||
|
||||
res.contentType = 'application/x-web-app-manifest+json';
|
||||
res.send(JSON.stringify(data));
|
||||
});
|
||||
|
||||
// Sample usage:
|
||||
// % curl 'http://localhost:5000/launch.html?https://mariobro.se'
|
||||
|
||||
// TODO: Serve each manifest from a separate subdomain.
|
||||
server.get({
|
||||
url: '/launch.html',
|
||||
swagger: {
|
||||
nickname: 'launch'
|
||||
notes: 'Launch webapp from a URL based on querystring'
|
||||
summary: 'Webapp Launcher',
|
||||
}
|
||||
}, function(req, res) {
|
||||
var app_url = req.url.split('?')[1];
|
||||
res.send("<script>window.location = '" + app_url + "';</script>");
|
||||
});
|
||||
};
|
|
@ -0,0 +1,66 @@
|
|||
var db = require('../.././db');
|
||||
var utils = require('../.././utils');
|
||||
|
||||
|
||||
module.exports = function(server) {
|
||||
// Sample usage:
|
||||
// % curl -X POST 'http://localhost:5000/game/submit' -d 'name=Mario Bros&app_url=http://mariobro.se&icons=128&screenshots=yes'
|
||||
server.post({
|
||||
url: '/game/submit',
|
||||
swagger: {
|
||||
nickname: 'submit',
|
||||
notes: 'Submit game',
|
||||
summary: 'Submission'
|
||||
},
|
||||
validation: {
|
||||
app_url: {
|
||||
description: 'App URL',
|
||||
isRequired: true,
|
||||
isUrl: true
|
||||
},
|
||||
homepage_url: {
|
||||
description: 'Homepage URL',
|
||||
isRequired: false,
|
||||
isUrl: true
|
||||
},
|
||||
icons: {
|
||||
description: 'Icons',
|
||||
isRequired: true,
|
||||
},
|
||||
name: {
|
||||
description: 'Name',
|
||||
isRequired: true,
|
||||
max: 128
|
||||
},
|
||||
screenshots: {
|
||||
description: 'Screenshots',
|
||||
isRequired: true
|
||||
}
|
||||
}
|
||||
}, function(req, res) {
|
||||
var POST = req.params;
|
||||
slug = utils.slugify(POST.slug || POST.name);
|
||||
var data = {
|
||||
app_url: POST.app_url,
|
||||
appcache_path: POST.appcache_path,
|
||||
default_locale: POST.default_locale,
|
||||
description: POST.description,
|
||||
developer_name: POST.developer_name,
|
||||
developer_url: POST.developer_url,
|
||||
fullscreen: POST.fullscreen,
|
||||
homepage_url: POST.homepage_url,
|
||||
icons: POST.icons,
|
||||
locales: POST.locales,
|
||||
name: POST.name,
|
||||
orientation: POST.orientation,
|
||||
|
||||
// Galaxy-specific metadata.
|
||||
license: POST.license,
|
||||
privacy: POST.privacy_policy,
|
||||
screenshots: POST.screenshots,
|
||||
slug: slug
|
||||
};
|
||||
db.flatfile.write('game', slug, data);
|
||||
res.json(data);
|
||||
});
|
||||
};
|
Загрузка…
Ссылка в новой задаче