Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: dockge set/update agent friendly name #414

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion backend/agent-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,15 @@ export class AgentManager {
* @param url
* @param username
* @param password
* @param friendlyname
* @param updatedFriendlyName
*/
async add(url : string, username : string, password : string) : Promise<Agent> {
async add(url : string, username : string, password : string, friendlyname : string) : Promise<Agent> {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Name Conventions

  • Javascript/Typescript: camelCaseType
  • SQLite: snake_case (Underscore)
  • CSS/SCSS: kebab-case (Dash)

https://github.com/louislam/dockge/blob/master/CONTRIBUTING.md#name-conventions

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all namings has been updated

let bean = R.dispense("agent") as Agent;
bean.url = url;
bean.username = username;
bean.password = password;
bean.friendlyname = friendlyname;
await R.store(bean);
return bean;
}
Expand All @@ -104,6 +107,16 @@ export class AgentManager {
}
}

/**
*
* @param friendlyname
* @param updatedFriendlyName
*/

async update(friendlyname : string, updatedFriendlyName : string) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would consider accepting the agent's id or the url here for identifying the agent to update instead of friendly name.

ID is the primary key, so thats probably best to use for updating the agent. But URL is always set and has a unique constraint, so it will work too.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with nathan, I would refactor to pass id around for the agent

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure it should be the id ? it is currently not passed in the agentList and needs to be added. If a host will be added and deleted it will still increase and not release a taken number. The url would be the "better" identifier ?
( just a small comment. I am not against refactoring this to id but while updating this MR I was thinking about)

await R.exec("UPDATE agent SET friendlyname = " + updatedFriendlyName + " WHERE friendlyname = " + friendlyname + "");

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SQL injection vuln here - if name has quotes this will break.

You can use parameter bindings to build queries safely:

await R.exec("UPDATE agent SET friendlyname = ? WHERE friendlyname = ?", [
    updatedFriendlyName,
    friendlyname
]);

https://github.com/louislam/redbean-node/blob/master/docs/Query.md

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of a raw query, you could utilize the redbean ORM methods. See: https://github.com/louislam/redbean-node/blob/master/docs/Create-Update-Bean.md

Something like this if by ID (primary key):

const agent = await R.load('agent', id);
agent.friendlyName = friendlyName;
await R.store(agent);

Or by URL:

const agent = await R.findOne('agent', ' url = ? ', [url]);
agent.friendlyName = friendlyName;
await R.store(agent);

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the input. Added this with update by url and started a small "discussion"
#414 (comment)

}

connect(url : string, username : string, password : string) {
let obj = new URL(url);
let endpoint = obj.host;
Expand Down Expand Up @@ -276,6 +289,8 @@ export class AgentManager {
url: "",
username: "",
endpoint: "",
friendlyname: "",
updatedFriendlyName: "",
};

for (let endpoint in list) {
Expand Down
1 change: 1 addition & 0 deletions backend/migrations/2023-12-20-2117-agent-table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export async function up(knex: Knex): Promise<void> {
table.string("url", 255).notNullable().unique();
table.string("username", 255).notNullable();
table.string("password", 255).notNullable();
table.string("friendlyname", 255);
Copy link
Owner

@louislam louislam Feb 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file cannot be modified. Have to create a new file.

Migration guide:
https://github.com/louislam/uptime-kuma/tree/master/db/knex_migrations

Also the naming convention, it should be friendly_name.

Will come back later, as I would like to finish the next milestone of Uptime Kuma first.

table.boolean("active").notNullable().defaultTo(true);
});
}
Expand Down
2 changes: 2 additions & 0 deletions backend/models/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ export class Agent extends BeanModel {
url: this.url,
username: this.username,
endpoint: this.endpoint,
friendlyname: this.friendlyname,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

recommendation: you should consider just naming this field name. There are no conflicts using the simpler version of the field name.

updatedFriendlyName: this.updatedFriendlyName

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is updatedFriendlyName also here on the model? Shouldn't just friendlyName be sufficient?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I forgot to update this file while adding the other suggestions but it still worked with the correct variables. Where is this needed 🤔

};
}

Expand Down
28 changes: 27 additions & 1 deletion backend/socket-handlers/manage-agent-socket-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export class ManageAgentSocketHandler extends SocketHandler {
let data = requestData as LooseObject;
let manager = socket.instanceManager;
await manager.test(data.url, data.username, data.password);
await manager.add(data.url, data.username, data.password);
await manager.add(data.url, data.username, data.password, data.friendlyname);

// connect to the agent
manager.connect(data.url, data.username, data.password);
Expand Down Expand Up @@ -66,5 +66,31 @@ export class ManageAgentSocketHandler extends SocketHandler {
callbackError(e, callback);
}
});

// updateAgent
socket.on("updateAgent", async (friendlyname : string, updatedFriendlyName : string, callback : unknown) => {
try {
log.debug("manage-agent-socket-handler", "updateAgent");
checkLogin(socket);

if (typeof(updatedFriendlyName) !== "string") {
throw new Error("FriendlyName must be a string");
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the more you know: since this is typescript, you shouldn't actually need to typecheck the input value for friendly name. We already know it's a string from before.


let manager = socket.instanceManager;
await manager.update(friendlyname, updatedFriendlyName);

server.disconnectAllSocketClients(undefined, socket.id);
manager.sendAgentList();

callbackResult({
ok: true,
msg: "agentUpdatedSuccessfully",
msgi18n: true,
}, callback);
} catch (e) {
callbackError(e, callback);
}
});
}
}
6 changes: 6 additions & 0 deletions frontend/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,11 @@ declare module 'vue' {
Appearance: typeof import('./src/components/settings/Appearance.vue')['default']
ArrayInput: typeof import('./src/components/ArrayInput.vue')['default']
ArraySelect: typeof import('./src/components/ArraySelect.vue')['default']
BButton: typeof import('bootstrap-vue-next')['BButton']
BDropdown: typeof import('bootstrap-vue-next')['BDropdown']
BDropdownItem: typeof import('bootstrap-vue-next')['BDropdownItem']
BFormGroup: typeof import('bootstrap-vue-next')['BFormGroup']
BFormInput: typeof import('bootstrap-vue-next')['BFormInput']
BModal: typeof import('bootstrap-vue-next')['BModal']
Confirm: typeof import('./src/components/Confirm.vue')['default']
Container: typeof import('./src/components/Container.vue')['default']
Expand All @@ -29,4 +32,7 @@ declare module 'vue' {
TwoFADialog: typeof import('./src/components/TwoFADialog.vue')['default']
Uptime: typeof import('./src/components/Uptime.vue')['default']
}
export interface ComponentCustomProperties {
vBModal: typeof import('bootstrap-vue-next')['vBModal']
}
}
3 changes: 2 additions & 1 deletion frontend/src/lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -112,5 +112,6 @@
"agentRemovedSuccessfully": "Agent removed successfully.",
"removeAgent": "Remove Agent",
"removeAgentMsg": "Are you sure you want to remove this agent?",
"LongSyntaxNotSupported": "Long syntax is not supported here. Please use the YAML editor."
"LongSyntaxNotSupported": "Long syntax is not supported here. Please use the YAML editor.",
"Friendly Name": "Friendly Name"
}
13 changes: 9 additions & 4 deletions frontend/src/mixins/socket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,10 +131,15 @@ export default defineComponent({
methods: {

endpointDisplayFunction(endpoint : string) {
if (endpoint) {
return endpoint;
} else {
return this.$t("currentEndpoint");
for (const [k, v] of Object.entries(this.$data.agentList)) {
if (endpoint) {
if (endpoint === v["endpoint"] && v["friendlyname"] !== "") {
return v["friendlyname"];
}
if (endpoint === v["endpoint"] && v["friendlyname"] === "" ) {
return endpoint;
}
}
}
},

Expand Down
2 changes: 1 addition & 1 deletion frontend/src/pages/Compose.vue
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@
<label for="name" class="form-label">{{ $t("dockgeAgent") }}</label>
<select v-model="stack.endpoint" class="form-select">
<option v-for="(agent, endpoint) in $root.agentList" :key="endpoint" :value="endpoint" :disabled="$root.agentStatusList[endpoint] != 'online'">
({{ $root.agentStatusList[endpoint] }}) {{ (endpoint) ? endpoint : $t("currentEndpoint") }}
({{ $root.agentStatusList[endpoint] }}) {{ (agent.friendlyname !== '') ? agent.friendlyname : agent.url || $t("Controller") }}
</option>
</select>
</div>
Expand Down
42 changes: 38 additions & 4 deletions frontend/src/pages/DashboardHome.vue
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,25 @@
</template>

<!-- Agent Display Name -->
<span v-if="endpoint === ''">{{ $t("currentEndpoint") }}</span>
<a v-else :href="agent.url" target="_blank">{{ endpoint }}</a>
<template v-if="$root.agentStatusList[endpoint]">
<span v-if="endpoint === '' && agent.friendlyname === ''" class="badge bg-secondary me-2">Controller</span>
<span v-else-if="agent.friendlyname === ''" :href="agent.url">{{ endpoint }}</span>
<span v-else :href="agent.url">{{ agent.friendlyname }}</span>
</template>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couple of suggestions here:

  1. We can use the fact that an empty string is falsy to shorten this chain.
  2. You probably don't want to be modifying things so drastically. If it's working, then a small change will do.
Suggested change
<template v-if="$root.agentStatusList[endpoint]">
<span v-if="endpoint === '' && agent.friendlyname === ''" class="badge bg-secondary me-2">Controller</span>
<span v-else-if="agent.friendlyname === ''" :href="agent.url">{{ endpoint }}</span>
<span v-else :href="agent.url">{{ agent.friendlyname }}</span>
</template>
<span v-if="endpoint === ''" class="badge bg-secondary me-2">{{ $t("currentEndpoint") }}</span>
<a v-else :href="agent.url" target="_blank">{{ agent.name || endpoint }}</a>


<!-- Edit FriendlyName -->
<font-awesome-icon v-if="agent.friendlyname !== '' && agent.friendlyname !== ''" icon="pen-to-square" @click="showEditAgentFriendlynameDialog[agent.friendlyname] = !showEditAgentFriendlynameDialog[agent.friendlyname]" />

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: I'm not sure why you're checking for blank string twice here, but if you switch to passing around agent.id you will be able to edit a name no matter if the server was created with friendly name or not, so the v-if will be unnecessary.


<!-- Edit Dialog -->
<BModal v-model="showEditAgentFriendlynameDialog[agent.friendlyname]" :no-close-on-backdrop="true" :close-on-esc="true" :okTitle="$t('Update Friendlyname')" okVariant="info" @ok="updateFriendlyname(agent.friendlyname, agent.updatedFriendlyName)">
<label for="Update Friendlyname" class="form-label">Current value: {{ $t(agent.friendlyname) }}</label>
<input id="updatedFriendlyName" v-model="agent.updatedFriendlyName" type="text" class="form-control" optional>
Copy link

@kayakyakr kayakyakr Jun 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: similar to nathan's comments up above, if you are finding agents by their ID, you can just set agent.name here and use that to update the value. You also then don't have to show Current value: in the label, because the input will have the current value. You may want to show agent.url as part of the label, though, in case input name is blank.

</BModal>

<!-- Remove Button -->
<font-awesome-icon v-if="endpoint !== ''" class="ms-2 remove-agent" icon="trash" @click="showRemoveAgentDialog[agent.url] = !showRemoveAgentDialog[agent.url]" />

<!-- Remoe Agent Dialog -->
<!-- Remove Agent Dialog -->
<BModal v-model="showRemoveAgentDialog[agent.url]" :okTitle="$t('removeAgent')" okVariant="danger" @ok="removeAgent(agent.url)">
<p>{{ agent.url }}</p>
{{ $t("removeAgentMsg") }}
Expand All @@ -81,6 +93,11 @@
<input id="password" v-model="agent.password" type="password" class="form-control" required autocomplete="new-password">
</div>

<div class="mb-3">
<label for="friendlyname" class="form-label">{{ $t("Friendly Name") }}</label>
<input id="friendlyname" v-model="agent.friendlyname" type="text" class="form-control" optional>
</div>

<button type="submit" class="btn btn-primary" :disabled="connectingAgent">
<template v-if="connectingAgent">{{ $t("connecting") }}</template>
<template v-else>{{ $t("connect") }}</template>
Expand Down Expand Up @@ -121,11 +138,14 @@ export default {
dockerRunCommand: "",
showAgentForm: false,
showRemoveAgentDialog: {},
showEditAgentFriendlynameDialog: {},
connectingAgent: false,
agent: {
url: "http://",
username: "",
password: "",
friendlyname: "",
updatedFriendlyName: "",
}
};
},
Expand Down Expand Up @@ -199,6 +219,20 @@ export default {
});
},

updateFriendlyname(friendlyname, updatedFriendlyName) {
//console.log(this.showEditAgentFriendlynameDialog.inputNewFriendlyName);
this.$root.getSocket().emit("updateAgent", friendlyname, updatedFriendlyName, (res) => {
this.$root.toastRes(res);

if (res.ok) {
this.showAgentForm = false;
this.agent = {
updatedFriendlyName: "",
};
}
});
},

getStatusNum(statusName) {
let num = 0;

Expand Down Expand Up @@ -286,7 +320,7 @@ export default {
}

},
},
}
};
</script>

Expand Down
Loading