mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-13 15:14:52 +09:00
Add notes to hosts, instances, users and other resources (#1000)
This commit is contained in:
1 parent
ee39945c83
commit
cbf2b10782
25 files changed
+830
-52
No files matched your search
@@ -119,6 +119,8 @@ export default class ControlConnection extends BaseConnection {
|
||||
this.handle(lib.RoleUpdateRequest, this.handleRoleUpdateRequest.bind(this));
|
||||
this.handle(lib.RoleGrantDefaultPermissionsRequest, this.handleRoleGrantDefaultPermissionsRequest.bind(this));
|
||||
this.handle(lib.RoleDeleteRequest, this.handleRoleDeleteRequest.bind(this));
|
||||
this.handle(lib.NoteListRequest, this.handleNoteListRequest.bind(this));
|
||||
this.handle(lib.NoteSetRequest, this.handleNoteSetRequest.bind(this));
|
||||
this.handle(lib.UserGetRequest, this.handleUserGetRequest.bind(this));
|
||||
this.handle(lib.UserListRequest, this.handleUserListRequest.bind(this));
|
||||
this.handle(lib.UserCreateRequest, this.handleUserCreateRequest.bind(this));
|
||||
@@ -313,6 +315,7 @@ export default class ControlConnection extends BaseConnection {
|
||||
|
||||
async handleInstanceDeleteRequest(request: lib.InstanceDeleteRequest) {
|
||||
await this._controller.instances.deleteInstance(request.instanceId);
|
||||
this._controller.deleteNote("instance", request.instanceId);
|
||||
}
|
||||
|
||||
async handleInstanceConfigGetRequest(request: lib.InstanceConfigGetRequest) {
|
||||
@@ -500,6 +503,7 @@ export default class ControlConnection extends BaseConnection {
|
||||
throw new lib.RequestError(`Mod pack with ID ${id} does not exist`);
|
||||
}
|
||||
this._controller.modPacks.delete(modPack);
|
||||
this._controller.deleteNote("mod_pack", id);
|
||||
}
|
||||
|
||||
async handleModGetRequest(request: lib.ModGetRequest) {
|
||||
@@ -863,6 +867,7 @@ export default class ControlConnection extends BaseConnection {
|
||||
|
||||
async handleModDeleteRequest(request: lib.ModDeleteRequest) {
|
||||
await this._controller.modStore.deleteMod(request.name, request.version);
|
||||
this._controller.deleteNote("mod", `${request.name}_${request.version}`);
|
||||
}
|
||||
|
||||
async handleLogSetSubscriptionsRequest(request: lib.LogSetSubscriptionsRequest) {
|
||||
@@ -973,6 +978,7 @@ export default class ControlConnection extends BaseConnection {
|
||||
throw new lib.RequestError(`Role with ID ${id} does not exist`);
|
||||
}
|
||||
this._controller.roles.delete(role);
|
||||
this._controller.deleteNote("role", id);
|
||||
|
||||
for (const user of this._controller.users.values()) {
|
||||
if (user.removeRole(id)) {
|
||||
@@ -981,6 +987,18 @@ export default class ControlConnection extends BaseConnection {
|
||||
}
|
||||
}
|
||||
|
||||
async handleNoteListRequest() {
|
||||
return [...this._controller.notes.values()].filter(note => !note.isDeleted);
|
||||
}
|
||||
|
||||
async handleNoteSetRequest(request: lib.NoteSetRequest) {
|
||||
const { resourceType, resourceId, content } = request;
|
||||
if (!resourceType || !resourceId) {
|
||||
throw new lib.RequestError("resourceType and resourceId must not be empty");
|
||||
}
|
||||
this._controller.setNote(resourceType, resourceId, content, this.user.name);
|
||||
}
|
||||
|
||||
async handleUserGetRequest(request: lib.UserGetRequest): Promise<lib.UserDetails> {
|
||||
let name = request.name;
|
||||
let user = this._controller.users.getByName(name);
|
||||
@@ -1095,6 +1113,7 @@ export default class ControlConnection extends BaseConnection {
|
||||
controlConnection.connector.terminate();
|
||||
}
|
||||
}
|
||||
this._controller.deleteNote("user", user.id);
|
||||
|
||||
if (user.isAdmin) {
|
||||
this._controller.sendTo("allInstances", new lib.InstanceAdminlistUpdateEvent(name, false));
|
||||
|
||||
@@ -205,6 +205,11 @@ export default class Controller {
|
||||
undefined, this.finaliseUsers,
|
||||
).bootstrap());
|
||||
|
||||
const notes = new lib.SubscribableDatastore(...await new lib.JsonIdDatastoreProvider(
|
||||
path.join(databaseDirectory, "notes.json"),
|
||||
lib.Note.fromJSON.bind(lib.Note),
|
||||
).bootstrap());
|
||||
|
||||
let modsDirectory = config.get("controller.mods_directory");
|
||||
await fs.mkdir(modsDirectory, { recursive: true });
|
||||
const modStore = await lib.ModStore.fromDirectory(modsDirectory);
|
||||
@@ -227,6 +232,7 @@ export default class Controller {
|
||||
modStore,
|
||||
roles,
|
||||
users,
|
||||
notes,
|
||||
] as const;
|
||||
}
|
||||
|
||||
@@ -260,6 +266,8 @@ export default class Controller {
|
||||
public roles = new lib.SubscribableDatastore<lib.Role>(),
|
||||
/** Mapping of user id to user record */
|
||||
users = new lib.SubscribableDatastore<UserRecord>(),
|
||||
/** Mapping of note id to note attached to a resource */
|
||||
public notes = new lib.SubscribableDatastore<lib.Note>(),
|
||||
) {
|
||||
this.clusterLogger = clusterLogger;
|
||||
this.pluginInfos = pluginInfos;
|
||||
@@ -291,6 +299,7 @@ export default class Controller {
|
||||
this.subscriptions.handle(lib.ModUpdatesEvent, this.handleModSubscription.bind(this));
|
||||
this.subscriptions.handle(lib.UserUpdatesEvent, this.handleUserSubscription.bind(this));
|
||||
this.subscriptions.handle(lib.RoleUpdatesEvent, this.handleRoleSubscription.bind(this));
|
||||
this.subscriptions.handle(lib.NoteUpdatesEvent, this.handleNoteSubscription.bind(this));
|
||||
|
||||
// Handle updates for datastores
|
||||
this.systems.on("update", this.systemsUpdated.bind(this));
|
||||
@@ -300,6 +309,7 @@ export default class Controller {
|
||||
this.modPacks.on("update", this.modPacksUpdated.bind(this));
|
||||
this.roles.on("update", this.rolesUpdated.bind(this));
|
||||
this.users.records.on("update", this.usersUpdated.bind(this));
|
||||
this.notes.on("update", this.notesUpdated.bind(this));
|
||||
}
|
||||
|
||||
get authSecret() {
|
||||
@@ -744,6 +754,7 @@ export default class Controller {
|
||||
this.roles.save(),
|
||||
this.users.records.save(),
|
||||
this.hooks.save.invoke(),
|
||||
this.notes.save(),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -1137,6 +1148,60 @@ export default class Controller {
|
||||
return roles.length ? new lib.RoleUpdatesEvent(roles) : null;
|
||||
}
|
||||
|
||||
notesUpdated(notes: lib.Note[]) {
|
||||
this.subscriptions.broadcast(new lib.NoteUpdatesEvent(notes));
|
||||
}
|
||||
|
||||
async handleNoteSubscription(request: lib.SubscriptionRequest) {
|
||||
if (request.action === "unsubscribe") {
|
||||
return null;
|
||||
}
|
||||
// Deleted notes are kept as tombstones so resubscribing clients learn of the deletion
|
||||
const notes = [...this.notes.values()].filter(
|
||||
note => note.updatedAtMs > request.lastRequestTimeMs && (request.lastRequestTimeMs > 0 || !note.isDeleted),
|
||||
);
|
||||
return notes.length ? new lib.NoteUpdatesEvent(notes) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set or remove the note attached to a resource
|
||||
* @param resourceType - Type of resource the note belongs to.
|
||||
* @param resourceId - Id of the resource within its type.
|
||||
* @param content - New text of the note, empty removes the note.
|
||||
* @param updatedBy - Name of the user making the change.
|
||||
*/
|
||||
setNote(resourceType: string, resourceId: string, content: string, updatedBy = "") {
|
||||
const existing = this.notes.getMutable(lib.Note.idFor(resourceType, resourceId));
|
||||
if (content === "") {
|
||||
this.deleteNote(resourceType, resourceId, updatedBy);
|
||||
return;
|
||||
}
|
||||
if (existing) {
|
||||
existing.content = content;
|
||||
existing.updatedBy = updatedBy;
|
||||
existing.isDeleted = false;
|
||||
this.notes.set(existing);
|
||||
} else {
|
||||
this.notes.set(new lib.Note(resourceType, resourceId, content, updatedBy));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the note attached to a resource, if any
|
||||
* @param resourceType - Type of resource the note belongs to.
|
||||
* @param resourceId - Id of the resource within its type.
|
||||
* @param updatedBy - Name of the user making the change.
|
||||
*/
|
||||
deleteNote(resourceType: string, resourceId: string | number, updatedBy = "") {
|
||||
const note = this.notes.getMutable(lib.Note.idFor(resourceType, resourceId));
|
||||
if (note && !note.isDeleted) {
|
||||
note.content = "";
|
||||
note.updatedBy = updatedBy;
|
||||
note.isDeleted = true;
|
||||
this.notes.set(note);
|
||||
}
|
||||
}
|
||||
|
||||
async loadPlugins() {
|
||||
const context = { metrics, logger, controller: this };
|
||||
for (let pluginInfo of this.pluginInfos) {
|
||||
|
||||
Reference in new issue
Block a user