Skip to content

Commit 0ed3cc3

Browse files
committed
[platform+module] app manager to enable app share
- import app from another NodeBase app manager server - manage local app - embed app manger files in NodeBase apk
1 parent 9ccbae8 commit 0ed3cc3

File tree

7 files changed

+597
-5
lines changed

7 files changed

+597
-5
lines changed
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package seven.drawalive.nodebase;
2+
3+
import android.content.Context;
4+
5+
import java.io.File;
6+
import java.io.IOException;
7+
import java.io.InputStream;
8+
9+
public class ModuleAppManager {
10+
public static String appManagerJs(Context context) {
11+
InputStream reader = context.getResources().openRawResource(R.raw.app_manager);
12+
try {
13+
byte[] buf = new byte[(int) reader.available()];
14+
reader.read(buf);
15+
return new String(buf);
16+
} catch (IOException e) {
17+
return null;
18+
} finally {
19+
if (reader != null) try { reader.close(); } catch (Exception e) {}
20+
}
21+
}
22+
23+
public static String appManagerReadme() {
24+
return "# NodeBase Application Manager\nrunning: 20180\nparams: (no params)\n";
25+
}
26+
27+
public static String appManagerConfig() {
28+
return "name=NodeBase Application Manager\nport=20180\n";
29+
}
30+
31+
public static void InstallAppManager(Context context, String workdir) {
32+
String appdir = workdir + "/app_manager";
33+
File dir = new File(appdir);
34+
if (dir.exists()) {
35+
return;
36+
}
37+
dir.mkdir();
38+
Storage.write(appManagerJs(context), appdir + "/index.js");
39+
Storage.write(appManagerReadme(), appdir + "/readme");
40+
Storage.write(appManagerConfig(), appdir + "/config");
41+
}
42+
}

app/app/src/main/java/seven/drawalive/nodebase/NodeBase.java

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,9 @@ protected void onDestroy() {
5151
@Override
5252
public boolean onCreateOptionsMenu(Menu menu) {
5353
menu.add(Menu.NONE, 101, Menu.NONE, "NICs");
54-
menu.add(Menu.NONE, 102, Menu.NONE, "Node Version");
55-
menu.add(Menu.NONE, 103, Menu.NONE, "Node Upgrade");
54+
menu.add(Menu.NONE, 110, Menu.NONE, "Install App Manager");
55+
menu.add(Menu.NONE, 120, Menu.NONE, "Node Version");
56+
menu.add(Menu.NONE, 121, Menu.NONE, "Node Upgrade");
5657
menu.add(Menu.NONE, 199, Menu.NONE, "Reset");
5758
return true;
5859
}
@@ -63,10 +64,13 @@ public boolean onOptionsItemSelected(MenuItem item) {
6364
case 101: // Show Network Interfaces
6465
showNicIps();
6566
break;
66-
case 102: // Show NodeJS Version
67+
case 110: // Install App Manager
68+
installAppManager();
69+
break;
70+
case 120: // Show NodeJS Version
6771
showNodeVersion();
6872
break;
69-
case 103: // Upgrade NodeJS
73+
case 121: // Upgrade NodeJS
7074
copyBinNodeFromNodebaseWorkdir();
7175
break;
7276
case 199: // Reset NodeJS
@@ -260,6 +264,12 @@ public void run() {
260264
}).act("Downlaod NodeJS", Configuration.NODE_URL, upgrade_node_filename);
261265
}
262266

267+
private void installAppManager() {
268+
String workdir = config.workDir();
269+
ModuleAppManager.InstallAppManager(this, workdir);
270+
Alarm.showToast(this, "successful");
271+
}
272+
263273
// state
264274
private Configuration config;
265275
private ArrayList<NodeBaseApp> _appList;

app/app/src/main/java/seven/drawalive/nodebase/NodeService.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,9 @@ public boolean stopService() {
136136

137137
public NodeMonitor restartService() {
138138
stopService();
139-
return new NodeMonitor(service_name, command);
139+
NodeMonitor m = new NodeMonitor(service_name, command);
140+
if (event != null) m.setEvent(event);
141+
return m;
140142
}
141143

142144
public boolean isRunning() {

modules/app_manager/config

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
name=NodeBase Application Manager
2+
port=20180

modules/app_manager/index.js

Lines changed: 268 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
1+
const http = require('http');
2+
const url = require('url');
3+
const mime = require('mime');
4+
const path = require('path');
5+
const fs = require('fs');
6+
7+
function get_ip (req) {
8+
let ip = null;
9+
if (req.headers['x-forwarded-for']) {
10+
ip = req.headers['x-forwarded-for'].split(",")[0];
11+
} else if (req.connection && req.connection.remoteAddress) {
12+
ip = req.connection.remoteAddress;
13+
} else {
14+
ip = req.ip;
15+
}
16+
return ip;
17+
}
18+
19+
function process_app_import(req, res, api, appname, appfiles, rename) {
20+
function random_tmp_name() {
21+
return 'tmp-' + Math.random();
22+
}
23+
function download_file(api, appname, appfiles, index, tmpdir, cb) {
24+
if (index >= appfiles.length) {
25+
cb();
26+
return;
27+
}
28+
if (!appfiles[index]) {
29+
download_file(api, appname, appfiles, index+1, tmpdir, cb);
30+
return;
31+
}
32+
let filename = appfiles[index];
33+
let tmpfile = path.join(tmpdir, filename);
34+
let subtmpdir = path.dirname(tmpfile);
35+
if (!fs.existsSync(subtmpdir)) {
36+
fs.mkdirSync(subtmpdir);
37+
}
38+
let file = fs.createWriteStream(tmpfile);
39+
let request = http.get(api + '/download/' + appname + '/' + filename, (obj) => {
40+
obj.pipe(file);
41+
download_file(api, appname, appfiles, index+1, tmpdir, cb);
42+
}).on('error', (e) => {
43+
errors.push('failed to download: ' + appfiles[index]);
44+
download_file(api, appname, appfiles, index+1, tmpdir, cb);
45+
});
46+
}
47+
let errors = [];
48+
let tmpdir = path.join(Storage.work_dir, random_tmp_name());
49+
let targetdir = path.join(Storage.work_dir, rename);
50+
if (fs.existsSync(targetdir)) {
51+
res.end('app exists: ' + rename);
52+
return;
53+
}
54+
fs.mkdirSync(tmpdir);
55+
download_file(api, appname, appfiles, 0, tmpdir, () => {
56+
if (errors.length > 0) {
57+
Storage.rmtree(tmpdir);
58+
res.end(errors.join('\n'));
59+
return;
60+
}
61+
fs.renameSync(tmpdir, targetdir);
62+
res.end('');
63+
});
64+
}
65+
66+
function route(req, res) {
67+
let r = url.parse(req.url);
68+
let f = router;
69+
let path = r.pathname.split('/');
70+
let query = {};
71+
r.query && r.query.split('&').forEach((one) => {
72+
let key, val;
73+
let i = one.indexOf('=');
74+
if (i < 0) {
75+
key = one;
76+
val = '';
77+
} else {
78+
key = one.substring(0, i);
79+
val = one.substring(i+1);
80+
}
81+
if (key in query) {
82+
if(Array.isArray(query[key])) {
83+
query[key].push(val);
84+
} else {
85+
query[key] = [query[key], val];
86+
}
87+
} else {
88+
query[key] = val;
89+
}
90+
});
91+
path.shift();
92+
while (path.length > 0) {
93+
let key = path.shift();
94+
f = f[key];
95+
if (!f) break;
96+
if (typeof(f) === 'function') {
97+
return f(req, res, {
98+
path: path,
99+
query: query
100+
});
101+
}
102+
}
103+
router.static(req, res, r.pathname);
104+
// router.code(req, res, 404, 'Not Found');
105+
}
106+
107+
const Storage = {
108+
work_dir: path.dirname(__dirname),
109+
list_directories: (dir) => {
110+
return fs.readdirSync(dir).filter((name) => {
111+
let subdir = path.join(dir, name);
112+
let state = fs.lstatSync(subdir);
113+
return state.isDirectory();
114+
});
115+
},
116+
list_files: (dir) => {
117+
let queue = [dir], list = [];
118+
while (queue.length > 0) {
119+
list_dir(queue.shift(), queue, list);
120+
}
121+
return list;
122+
123+
function list_dir(dir, queue, list) {
124+
fs.readdirSync(dir).forEach((name) => {
125+
let filename = path.join(dir, name);
126+
let state = fs.lstatSync(filename);
127+
if (state.isDirectory()) {
128+
queue.push(filename);
129+
} else {
130+
list.push(filename);
131+
}
132+
});
133+
}
134+
},
135+
rmtree: (dir) => {
136+
if (dir.length < Storage.work_dir.length) {
137+
return false;
138+
}
139+
if (dir.indexOf(Storage.work_dir) !== 0) {
140+
return false;
141+
}
142+
if (!fs.existsSync(dir)) {
143+
return false;
144+
}
145+
fs.readdirSync(dir).forEach(function(file, index){
146+
var curPath = path.join(dir, file);
147+
if (fs.lstatSync(curPath).isDirectory()) {
148+
// recurse
149+
rmtree(curPath);
150+
} else { // delete file
151+
fs.unlinkSync(curPath);
152+
}
153+
});
154+
fs.rmdirSync(dir);
155+
return true;
156+
}
157+
};
158+
159+
const router = {
160+
app: {
161+
list: (req, res, options) => {
162+
let dir = path.dirname(__dirname);
163+
if (!fs.existsSync(dir)) {
164+
return router.code(req, res, 404, 'Not Found');
165+
}
166+
let names = Storage.list_directories(Storage.work_dir).filter((name) => {
167+
if (name.startsWith('.')) return false;
168+
if (name === 'node_modules') return false;
169+
return true;
170+
});
171+
res.end(names.join('\n'));
172+
},
173+
files: (req, res, options) => {
174+
let name = options.path[0];
175+
if (!name) {
176+
return router.code(req, res, 404, 'Not Found');
177+
}
178+
let subdir = path.join(Storage.work_dir, name);
179+
if (!fs.existsSync(subdir)) {
180+
return router.code(req, res, 404, 'Not Found');
181+
}
182+
let files = Storage.list_files(subdir).map((filename) => {
183+
return filename.substring(subdir.length+1);
184+
});
185+
res.end(files.join('\n'));
186+
},
187+
download: (req, res, options) => {
188+
if (options.path.indexOf('..') >= 0) {
189+
return router.code(req, res, 404, 'Not Found');
190+
}
191+
let filename = path.join(...options.path);
192+
filename = path.join(Storage.work_dir, filename);
193+
if (!fs.existsSync(filename)) {
194+
return router.code(req, res, 404, 'Not Found');
195+
}
196+
res.setHeader('Content-Disposition', 'attachment; filename=' + path.basename(filename));
197+
//res.setHeader('Content-Type', 'application/octet-stream');
198+
res.setHeader('Content-Type', 'text/plain');
199+
let buf = fs.readFileSync(filename);
200+
res.end(buf, 'binary');
201+
},
202+
delete: (req, res, options) => {
203+
let name = options.path[0];
204+
if (!name) {
205+
return router.code(req, res, 404, 'Not Found');
206+
}
207+
let filename = path.join(Storage.work_dir, name);
208+
// !!!! dangerous action
209+
Storage.rmtree(filename);
210+
res.end('');
211+
},
212+
import: (req, res, options) => {
213+
// options.path [ip:port, appname, rename]
214+
let host = options.path[0];
215+
let appname = options.path[1];
216+
let name = options.path[2] || appname;
217+
let api = 'http://' + host + '/app';
218+
http.get(api + '/files/' + appname, (obj) => {
219+
if (~~(obj.statusCode/100) !== 2) {
220+
obj.resume();
221+
return router.code(req, res, 404, 'Not Found');
222+
}
223+
let raw = '';
224+
obj.on('data', (chunk) => { raw += chunk; });
225+
obj.on('end', () => {
226+
process_app_import(req, res, api, appname, raw.split('\n'), name);
227+
});
228+
}).on('error', (e) => {
229+
return router.code(req, res, 404, 'Not Found');
230+
});
231+
}
232+
},
233+
test: (req, res, options) => {
234+
res.end('hello');
235+
},
236+
static: (req, res, filename) => {
237+
if (!filename || filename === '/') {
238+
filename = 'index.html';
239+
}
240+
filename = filename.split('/');
241+
if (!filename[0]) filename.shift();
242+
if (filename.length === 0 || filename.indexOf('..') >= 0) {
243+
return router.code(req, res, 404, 'Not Found');
244+
}
245+
filename = path.join(__dirname, 'static', ...filename);
246+
if (!fs.existsSync(filename)) {
247+
return router.code(req, res, 404, 'Not Found');
248+
}
249+
res.setHeader('Content-Type', mime.lookup(filename));
250+
let buf = fs.readFileSync(filename);
251+
res.end(buf, 'binary');
252+
},
253+
code: (req, res, code, text) => {
254+
res.writeHead(code || 404, text || '');
255+
res.end();
256+
}
257+
};
258+
259+
init();
260+
261+
const server = http.createServer((req, res) => {
262+
route(req, res);
263+
});
264+
265+
const instance = server.listen(20180, '0.0.0.0', () => {
266+
console.log(instance.address());
267+
console.log(`NodeBase Application Manager is listening at 0.0.0.0:20180`);
268+
});

modules/app_manager/readme

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# NodeBase Application Manager
2+
running: 20180
3+
params: (no params)

0 commit comments

Comments
 (0)