Skip to content

Commit 0de12ad

Browse files
committed
Add fsgrid support, part 1
1 parent 13d6694 commit 0de12ad

7 files changed

Lines changed: 238 additions & 4 deletions

File tree

OpenFlow/src/DatabaseConnection.ts

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {
2-
ObjectID, Db, Binary, InsertOneWriteOpResult, DeleteWriteOpResultObject, ObjectId, MapReduceOptions, CollectionInsertOneOptions, UpdateWriteOpResult, WriteOpResult
2+
ObjectID, Db, Binary, InsertOneWriteOpResult, DeleteWriteOpResultObject, ObjectId, MapReduceOptions, CollectionInsertOneOptions, UpdateWriteOpResult, WriteOpResult, GridFSBucket
33
} from "mongodb";
44
import { MongoClient } from "mongodb";
55
import { Base, Rights, WellknownIds } from "./base";
@@ -23,7 +23,7 @@ const isoDatePattern = new RegExp(/\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\
2323
export class DatabaseConnection {
2424
private mongodburl: string;
2525
private cli: MongoClient;
26-
private db: Db;
26+
public db: Db;
2727
private _logger: winston.Logger;
2828
private _dbname: string;
2929
constructor(logger: winston.Logger, mongodburl: string, dbname: string) {
@@ -106,6 +106,7 @@ export class DatabaseConnection {
106106
if (collectionname === "files") { collectionname = "fs.files"; }
107107
if (collectionname === "fs.files") {
108108
_query = { $and: [query, this.getbasequery(jwt, "metadata._acl", [Rights.read])] };
109+
projection = null;
109110
} else {
110111
if (!collectionname.endsWith("_hist")) {
111112
_query = { $and: [query, this.getbasequery(jwt, "_acl", [Rights.read])] };
@@ -557,6 +558,21 @@ export class DatabaseConnection {
557558
}
558559
return q;
559560
}
561+
private async _DeleteFile(id: string): Promise<string> {
562+
return new Promise<string>(async (resolve, reject) => {
563+
try {
564+
var _id = new ObjectID(id);
565+
var bucket = new GridFSBucket(this.db);
566+
bucket.delete(_id, (error) => {
567+
if (error) return reject(error);
568+
resolve();
569+
})
570+
} catch (err) {
571+
reject(err);
572+
}
573+
});
574+
}
575+
560576
/**
561577
* @param {string} id id of object to delete
562578
* @param {string} collectionname collectionname Collection containing item
@@ -579,6 +595,18 @@ export class DatabaseConnection {
579595
//_query = { $and: [{ _id: { $ne: user._id } }, _query] };
580596
}
581597

598+
if (collectionname === "files") { collectionname = "fs.files"; }
599+
if (collectionname === "fs.files") {
600+
_query = { $and: [{ _id: safeObjectID(id) }, this.getbasequery(jwt, "metadata._acl", [Rights.delete])] };
601+
var arr = await this.db.collection(collectionname).find(_query).toArray();
602+
if (arr.length == 1) {
603+
await this._DeleteFile(id);
604+
return;
605+
} else {
606+
throw Error("item not found!");
607+
}
608+
}
609+
582610

583611
// var arr = await this.db.collection(collectionname).find(_query).toArray();
584612

OpenFlow/src/Messages/Message.ts

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ import { DeleteNoderedInstanceMessage } from "./DeleteNoderedInstanceMessage";
3030
import { GetNoderedInstanceMessage } from "./GetNoderedInstanceMessage";
3131
import { GetNoderedInstanceLogMessage } from "./GetNoderedInstanceLogMessage";
3232
import { Util } from "../Util";
33+
import { SaveFileMessage } from "./SaveFileMessage";
34+
import { Readable, Stream } from "stream";
35+
import { GridFSBucket } from "mongodb";
3336

3437
export class Message {
3538
public id: string;
@@ -150,6 +153,9 @@ export class Message {
150153
case "stopnoderedinstance":
151154
this.StopNoderedInstance(cli);
152155
break;
156+
case "savefile":
157+
this.SaveFile(cli);
158+
break;
153159
default:
154160
this.UnknownCommand(cli);
155161
break;
@@ -941,6 +947,79 @@ export class Message {
941947
this.Send(cli);
942948
}
943949

950+
private async _SaveFile(stream: Stream, filename: string, contentType: string, metadata: Base): Promise<string> {
951+
return new Promise<string>(async (resolve, reject) => {
952+
try {
953+
var bucket = new GridFSBucket(Config.db.db);
954+
let uploadStream = bucket.openUploadStream(filename, { contentType: contentType, metadata: metadata });
955+
let id = uploadStream.id;
956+
stream.pipe(uploadStream);
957+
uploadStream.on('error', function (error) {
958+
reject(error);
959+
}).
960+
on('finish', function () {
961+
resolve(id.toString());
962+
});
963+
} catch (err) {
964+
reject(err);
965+
}
966+
});
967+
}
968+
private async SaveFile(cli: WebSocketClient): Promise<void> {
969+
this.Reply();
970+
var msg: SaveFileMessage
971+
try {
972+
msg = SaveFileMessage.assign(this.data);
973+
if (Util.IsNullEmpty(msg.jwt)) { msg.jwt = cli.jwt; }
974+
if (Util.IsNullEmpty(msg.filename)) throw new Error("Filename is mandatory");
975+
if (Util.IsNullEmpty(msg.mimeType)) throw new Error("mimeTypes is mandatory");
976+
if (Util.IsNullEmpty(msg.file)) throw new Error("file is mandatory");
977+
978+
var buf = Buffer.from(msg.file, 'base64');
979+
var readable = new Readable();
980+
readable._read = () => { }; // _read is required but you can noop it
981+
readable.push(buf);
982+
readable.push(null);
983+
if (msg.metadata == null) { msg.metadata = new Base(); }
984+
msg.metadata = Base.assign(msg.metadata);
985+
if (Util.IsNullUndefinded(msg.metadata._acl)) { msg.metadata._acl = []; }
986+
var user: TokenUser = Crypt.verityToken(msg.jwt);
987+
if (!Config.db.hasAuthorization(user, msg.metadata, "create")) { throw new Error("Access denied"); }
988+
msg.metadata._createdby = user.name;
989+
msg.metadata._createdbyid = user._id;
990+
msg.metadata._created = new Date(new Date().toISOString());
991+
msg.metadata._modifiedby = user.name;
992+
msg.metadata._modifiedbyid = user._id;
993+
msg.metadata._modified = msg.metadata._created;
994+
if (Util.IsNullEmpty(msg.metadata.name)) {
995+
msg.metadata.name = msg.filename;
996+
}
997+
var hasUser: any = msg.metadata._acl.find(e => e._id === user._id);
998+
if ((hasUser === null || hasUser === undefined)) {
999+
msg.metadata.addRight(user._id, user.name, [Rights.full_control]);
1000+
}
1001+
hasUser = msg.metadata._acl.find(e => e._id === WellknownIds.filestore_admins);
1002+
if ((hasUser === null || hasUser === undefined)) {
1003+
msg.metadata.addRight(WellknownIds.filestore_admins, "filestore admins", [Rights.full_control]);
1004+
}
1005+
hasUser = msg.metadata._acl.find(e => e._id === WellknownIds.filestore_users);
1006+
if ((hasUser === null || hasUser === undefined)) {
1007+
msg.metadata.addRight(WellknownIds.filestore_users, "filestore users", [Rights.read]);
1008+
}
1009+
msg.id = await this._SaveFile(readable, msg.filename, msg.mimeType, msg.metadata);
1010+
} catch (error) {
1011+
if (Util.IsNullUndefinded(msg)) { (msg as any) = {}; }
1012+
msg.error = error.toString();
1013+
cli._logger.error(error);
1014+
}
1015+
try {
1016+
this.data = JSON.stringify(msg);
1017+
} catch (error) {
1018+
this.data = "";
1019+
cli._logger.error(error);
1020+
}
1021+
this.Send(cli);
1022+
}
9441023
}
9451024

9461025
export class JSONfn {
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { Base } from "../base";
2+
3+
export class SaveFileMessage implements IReplyMessage {
4+
public error: string;
5+
public jwt: any;
6+
public filename: string;
7+
public file: string;
8+
public mimeType: string;
9+
public metadata: Base;
10+
public id: string;
11+
12+
static assign(o: any): SaveFileMessage {
13+
if (typeof o === "string" || o instanceof String) {
14+
return Object.assign(new SaveFileMessage(), JSON.parse(o.toString()));
15+
}
16+
return Object.assign(new SaveFileMessage(), o);
17+
}
18+
}

OpenFlow/src/index.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,11 +83,23 @@ async function initDatabase(): Promise<boolean> {
8383
robot_users.removeRight(WellknownIds.admins, [Rights.delete]);
8484
await robot_users.Save(jwt);
8585

86-
8786
if (!admins.IsMember(root._id)) {
8887
admins.AddMember(root);
8988
await admins.Save(jwt);
9089
}
90+
91+
var filestore_admins: Role = await User.ensureRole(jwt, "filestore admins", WellknownIds.filestore_admins);
92+
filestore_admins.AddMember(admins);
93+
filestore_admins.addRight(WellknownIds.admins, "admins", [Rights.full_control]);
94+
filestore_admins.removeRight(WellknownIds.admins, [Rights.delete]);
95+
await filestore_admins.Save(jwt);
96+
var filestore_users: Role = await User.ensureRole(jwt, "filestore users", WellknownIds.filestore_users);
97+
filestore_users.AddMember(admins);
98+
filestore_users.AddMember(users);
99+
filestore_users.addRight(WellknownIds.admins, "admins", [Rights.full_control]);
100+
filestore_users.removeRight(WellknownIds.admins, [Rights.delete]);
101+
await filestore_users.Save(jwt);
102+
91103
return true;
92104
} catch (error) {
93105
logger.error(error);

OpenFlow/src/public/Controllers.ts

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -978,7 +978,51 @@ module openflow {
978978

979979

980980

981-
981+
export class FilesCtrl extends entitiesCtrl<openflow.Base> {
982+
constructor(
983+
public $scope: ng.IScope,
984+
public $location: ng.ILocationService,
985+
public $routeParams: ng.route.IRouteParamsService,
986+
public $interval: ng.IIntervalService,
987+
public WebSocketClient: WebSocketClient,
988+
public api: api
989+
) {
990+
super($scope, $location, $routeParams, $interval, WebSocketClient, api);
991+
console.debug("EntitiesCtrl");
992+
this.autorefresh = true;
993+
this.autorefreshinterval = 15000;
994+
this.basequery = {};
995+
this.collection = "files";
996+
this.baseprojection = { _type: 1, type: 1, name: 1, _created: 1, _createdby: 1, _modified: 1 };
997+
WebSocketClient.onSignedin((user: TokenUser) => {
998+
this.loadData();
999+
});
1000+
}
1001+
async DeleteOne(model: any): Promise<any> {
1002+
this.loading = true;
1003+
await this.api.Delete(this.collection, model);
1004+
this.models = this.models.filter(function (m: any): boolean { return m._id !== model._id; });
1005+
this.loading = false;
1006+
if (!this.$scope.$$phase) { this.$scope.$apply(); }
1007+
}
1008+
async DeleteMany(): Promise<void> {
1009+
this.loading = true;
1010+
var Promises: Promise<DeleteOneMessage>[] = [];
1011+
var q: DeleteOneMessage = new DeleteOneMessage();
1012+
this.models.forEach(model => {
1013+
q.collectionname = this.collection; q._id = (model as any)._id;
1014+
var msg: Message = new Message(); msg.command = "deleteone"; msg.data = JSON.stringify(q);
1015+
Promises.push(this.WebSocketClient.Send(msg));
1016+
});
1017+
const results: any = await Promise.all(Promises.map(p => p.catch(e => e)));
1018+
const values: DeleteOneMessage[] = results.filter(result => !(result instanceof Error));
1019+
var ids: string[] = [];
1020+
values.forEach((x: DeleteOneMessage) => ids.push(x._id));
1021+
this.models = this.models.filter(function (m: any): boolean { return ids.indexOf(m._id) === -1; });
1022+
this.loading = false;
1023+
if (!this.$scope.$$phase) { this.$scope.$apply(); }
1024+
}
1025+
}
9821026
export class EntitiesCtrl extends entitiesCtrl<openflow.Base> {
9831027
constructor(
9841028
public $scope: ng.IScope,

OpenFlow/src/public/Files.html

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<div class="row">
2+
<h1 translate lib="web">files</h1>
3+
<p class="col-md-5 lead"></p>
4+
<div class="form-group col-sm-3 form-horizontal">
5+
<label class="sr-only">Filter</label>
6+
<div class="input-group unframed-addons">
7+
<div class="input-group-addon"><i class="fas fa-search"></i></div>
8+
<input ng-model="ctrl.searchstring" ng-change="ctrl.Search()" class="form-control input-md"
9+
ng-model-options="{debounce: 400}" />
10+
</div>
11+
</div>
12+
</div>
13+
14+
15+
<div class="row">
16+
<div class="col-md-6">
17+
</div>
18+
<div class=" col-md-6 text-right">
19+
<!-- <a ng-href="#/Entity/{{ctrl.collection}}" class="btn btn-info" translate lib="web">addentity</a> -->
20+
<a ng-href="#/Entity/{{ctrl.collection}}" class="btn btn-info"><i class="az-add-lg"></i></a>
21+
</div>
22+
</div>
23+
24+
<table id="table1" class="table table-striped table-hover table-sm" when-scrolled="ctrl.more()" style="width: 100%;">
25+
<thead class="thead-dark">
26+
<tr>
27+
<th scope="col" ng-click="ctrl.ToggleOrder('filename')"><b translate lib="web">name</b></th>
28+
<th scope="col" ng-click="ctrl.ToggleOrder('contentType')"><b translate lib="web">type</b></th>
29+
<th scope="col" ng-click="ctrl.ToggleOrder('metadata._createdby')"><b translate lib="web">createdby</b></th>
30+
<th scope="col" ng-click="ctrl.ToggleOrder('metadata._created')"><b translate lib="web">created</b></th>
31+
<th></th>
32+
<th></th>
33+
</tr>
34+
</thead>
35+
<tbody>
36+
<tr ng-repeat="model in ctrl.models">
37+
<td>{{model.filename}}</td>
38+
<td>{{model.contentType}}</td>
39+
<td>{{model.metadata._createdby}}</td>
40+
<td>
41+
<timesince ng-model="model.metadata._created" />
42+
</td>
43+
<td class="btn-cell">
44+
<a ng-href="#/Entity/{{ctrl.collection}}/{{model._id}}" class="table-btn"><i class="az-edit"></i></a>
45+
</td>
46+
<td class="btn-cell">
47+
<a href ng-click="ctrl.DeleteOne(model)" ng-disabled="ctrl.loading==true" class="table-btn"><i
48+
class="az-trash"></i></a>
49+
</td>
50+
</tr>
51+
</tbody>
52+
</table>

OpenFlow/src/public/app.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ module openflow {
4343
.when('/Form/:id', { templateUrl: 'Form.html', controller: FormCtrl, controllerAs: 'ctrl' })
4444
.when('/Form/:id/:instance', { templateUrl: 'Form.html', controller: FormCtrl, controllerAs: 'ctrl' })
4545

46+
.when('/Files', { templateUrl: 'Files.html', controller: FilesCtrl, controllerAs: 'ctrl' })
4647
.when('/Entities/:collection', { templateUrl: 'Entities.html', controller: EntitiesCtrl, controllerAs: 'ctrl' })
4748
.when('/Entity/:collection', { templateUrl: 'Entity.html', controller: EntityCtrl, controllerAs: 'ctrl' })
4849
.when('/Entity/:collection/:id', { templateUrl: 'Entity.html', controller: EntityCtrl, controllerAs: 'ctrl' })

0 commit comments

Comments
 (0)