From 10968065aa1e41ea97fa57a0d773067248036ae8 Mon Sep 17 00:00:00 2001 From: Dan Livings Date: Thu, 2 Jan 2025 13:24:34 +0000 Subject: [PATCH] Refactor and expand Nunjucks templates Express introduces the concept of views and rendering, which simplifies the process of producing an HTML response. It's straightforward to use Nunjucks as the rendering engine as the `nunjucks.render` method has the same signature as the `callback` passed to `Express.engine`. The `views/` folder is now the location in which Nunjucks templates should be stored, and it currently contains: - `_base.njk`: the base template that all others should inherit from. It sets up the basic structure and styling. - `_macros.njk`: basic repeatable utilities. - `index.njk`: Originally the dashboard, this has now been repurposed to display a list of organisations that the logged in user has access to in preparation for authentication being implemented. - `login.njk`: The login page. This will be displayed to the user by default if they are not logged in. It can be configured to offer either GitHub logins or username/password logins (or both). - `org-dashboard.njk`: The original dashboard template, repurposed to allow scoping by individual organisation. - `404.njk`: The page displayed when the server has no route defined for the requested URL. --- index.js | 11 +- index.njk | 220 -------------------------------- views/404.njk | 11 ++ views/_base.njk | 30 +++++ macros.njk => views/_macros.njk | 2 +- views/index.njk | 26 ++++ views/login.njk | 28 ++++ views/org-dashboard.njk | 200 +++++++++++++++++++++++++++++ 8 files changed, 305 insertions(+), 223 deletions(-) delete mode 100644 index.njk create mode 100644 views/404.njk create mode 100644 views/_base.njk rename macros.njk => views/_macros.njk (98%) create mode 100644 views/index.njk create mode 100644 views/login.njk create mode 100644 views/org-dashboard.njk diff --git a/index.js b/index.js index 0a3b424..1756a06 100644 --- a/index.js +++ b/index.js @@ -13,6 +13,11 @@ nunjucks.configure({ const httpServer = express(); httpServer.use(handleWebhooks); +httpServer.engine("njk", nunjucks.render); + +httpServer.set("views", "./views"); +httpServer.set("view engine", "njk"); + httpServer.get("/", (request, response) => { const db = new TowtruckDatabase(); const persistedRepoData = db.getAllRepositories(); @@ -22,14 +27,16 @@ httpServer.get("/", (request, response) => { const { sortDirection, sortBy } = request.query; - const template = nunjucks.render("index.njk", { + return response.render("org-dashboard", { sortBy, sortDirection, ...reposForUi, repos: sortByType(reposForUi.repos, sortDirection, sortBy), }); +}); - return response.end(template); +httpServer.all("*path", (request, response) => { + return response.status(404).render("404", { url: request.path }); }); const PORT = process.env.PORT || 3000; diff --git a/index.njk b/index.njk deleted file mode 100644 index 184a09a..0000000 --- a/index.njk +++ /dev/null @@ -1,220 +0,0 @@ -{% import "./macros.njk" as macros %} - - - - - - - Towtruck - - - - - - -
- -
- -
-
-

{{ org }}

-

- {% if totalRepos === 1 %} - There is 1 repository - {% else %} - There are {{ totalRepos }} repositories - {% endif %} - that Towtruck is tracking for {{ org }}. -

-
- - - - - {{macros.sortableTableHeader("Open issues count", "openIssues", sortDirection, sortBy)}} - {{macros.sortableTableHeader("Open bot PR count", "openBotPrCount", sortDirection, sortBy)}} - {{macros.sortableTableHeader("Open PR count", "openPrCount", sortDirection, sortBy)}} - {{macros.sortableTableHeader("Updated at", "updatedAt", sortDirection, sortBy)}} - {{macros.sortableTableHeader("Most recent PR opened", "mostRecentPrOpenedAt", sortDirection, sortBy)}} - {{macros.sortableTableHeader("Oldest open PR opened", "oldestOpenPrOpenedAt", sortDirection, sortBy)}} - {{macros.sortableTableHeader("Most recent issue opened", "mostRecentIssueOpenedAt", sortDirection, sortBy)}} - {{macros.sortableTableHeader("Oldest open issue opened", "oldestOpenIssueOpenedAt", sortDirection, sortBy)}} - - - - {% for repo in repos %} - - - - - - - - - - - - - - {% else %} - - - - {% endfor %} - -
Repository
-
- {{ repo.name }} - {% if repo.language %} - {{ repo.language }} - {% endif %} - {% if repo.totalOpenAlerts !== undefined %} -
- {% set id = [repo.name, "alerts", "critical"] | join("-") %} - {% set mainText = ["", repo.criticalSeverityAlerts, ""] | join('') | safe %} -
{{macros.tooltip(id, mainText, "Critical severity alerts")}}
- {% set id = [repo.name, "alerts", "high"] | join("-") %} - {% set mainText = ["", repo.highSeverityAlerts, ""] | join('') | safe %} -
{{macros.tooltip(id, mainText, "High severity alerts")}}
- {% set id = [repo.name, "alerts", "medium"] | join("-") %} - {% set mainText = ["", repo.mediumSeverityAlerts, ""] | join('') | safe %} -
{{macros.tooltip(id, mainText, "Medium severity alerts")}}
- {% set id = [repo.name, "alerts", "low"] | join("-") %} - {% set mainText = ["", repo.lowSeverityAlerts, ""] | join('') | safe %} -
{{macros.tooltip(id, mainText, "Low severity alerts")}}
-
- {% else %} - Dependabot alerts have been disabled for this repository. - {% endif %} -
-

{{ repo.description }}

- - {% if repo.topics.length %} -
- {% for topic in repo.topics %} - {{ topic }} - {% endfor %} -
- {% endif %} - -
- -
- {% if repo.dependencies.length %} -
-

- {% if repo.dependencies.length === 1 %} - There is 1 dependency: - {% else %} - There are {{ repo.dependencies.length }} dependencies: - {% endif %} - -

- -
- {% else %} - - No dependencies have been discovered. -
- Please make sure that Renovate has been configured on the repository to produce a dependency dashboard. -
- {% endif %} -
-
- - - {{ repo.openIssues }} - - - - - - {{ repo.openBotPrCount }} - - - - - - {{ repo.openPrCount }} - - - - - - {{ repo.updatedAt }} - - - - - - {{ repo.mostRecentPrOpenedAt }} - - - - - - {{ repo.oldestOpenPrOpenedAt }} - - - - - - {{ repo.mostRecentIssueOpenedAt }} - - - - - - {{ repo.oldestOpenIssueOpenedAt }} - - -
No repositories found.
-
-
-
- - diff --git a/views/404.njk b/views/404.njk new file mode 100644 index 0000000..c782a31 --- /dev/null +++ b/views/404.njk @@ -0,0 +1,11 @@ +{% extends "./_base.njk" %} + +{% block content %} +
+
The requested page {{ url }} could not be found.
+
+ +
+{% endblock %} diff --git a/views/_base.njk b/views/_base.njk new file mode 100644 index 0000000..9cd3143 --- /dev/null +++ b/views/_base.njk @@ -0,0 +1,30 @@ +{% import "./_macros.njk" as macros %} + + + + + + + Towtruck + + + + + + +
+ +
+ +
+ {% block content %} + {% endblock %} +
+ + diff --git a/macros.njk b/views/_macros.njk similarity index 98% rename from macros.njk rename to views/_macros.njk index 0da7be3..987e3a1 100644 --- a/macros.njk +++ b/views/_macros.njk @@ -29,7 +29,7 @@ - {{name}} + {{name}} {% if sortDirection === "asc" and sortingOnThisParam %} diff --git a/views/index.njk b/views/index.njk new file mode 100644 index 0000000..cf7597f --- /dev/null +++ b/views/index.njk @@ -0,0 +1,26 @@ +{% extends "./_base.njk" %} + +{% block content %} +
+

Organisations

+ {% if orgs.length %} +

+ {% if orgs.length === 1 %} + There is 1 organisation + {% else %} + There are {{ orgs.length }} organisations + {% endif %} + using Towtruck that you are a member of. +

+
+ {% else %} +

+ You are not a member of any organisations that are using Towtruck. +

+ {% endif %} +
+{% endblock %} \ No newline at end of file diff --git a/views/login.njk b/views/login.njk new file mode 100644 index 0000000..f8b103e --- /dev/null +++ b/views/login.njk @@ -0,0 +1,28 @@ +{% extends "./_base.njk" %} + +{% block content %} +
+
To use Towtruck, you must login.
+ {% if loginMethods.github %} +
+ + {% endif %} + {% if loginMethods.usernamePassword %} +
+
+
+ + + + + +
+
+ {% endif %} +
+{% endblock %} diff --git a/views/org-dashboard.njk b/views/org-dashboard.njk new file mode 100644 index 0000000..e2604eb --- /dev/null +++ b/views/org-dashboard.njk @@ -0,0 +1,200 @@ +{% extends "./_base.njk" %} + +{% block content %} +
+

{{ org }}

+

+ {% if totalRepos === 1 %} + There is 1 repository + {% else %} + There are {{ totalRepos }} repositories + {% endif %} + that Towtruck is tracking for {{ org }}. +

+
+ + + + + {{macros.sortableTableHeader("Open issues count", "openIssues", sortDirection, sortBy)}} + {{macros.sortableTableHeader("Open bot PR count", "openBotPrCount", sortDirection, sortBy)}} + {{macros.sortableTableHeader("Open PR count", "openPrCount", sortDirection, sortBy)}} + {{macros.sortableTableHeader("Updated at", "updatedAt", sortDirection, sortBy)}} + {{macros.sortableTableHeader("Most recent PR opened", "mostRecentPrOpenedAt", sortDirection, sortBy)}} + {{macros.sortableTableHeader("Oldest open PR opened", "oldestOpenPrOpenedAt", sortDirection, sortBy)}} + {{macros.sortableTableHeader("Most recent issue opened", "mostRecentIssueOpenedAt", sortDirection, sortBy)}} + {{macros.sortableTableHeader("Oldest open issue opened", "oldestOpenIssueOpenedAt", sortDirection, sortBy)}} + + + + {% for repo in repos %} + + + + + + + + + + + + + + {% else %} + + + + {% endfor %} + +
Repository
+
+ {{ repo.name }} + {% if repo.language %} + {{ repo.language }} + {% endif %} + {% if repo.totalOpenAlerts !== undefined %} +
+ {% set id = [repo.name, "alerts", "critical"] | join("-") %} + {% set mainText = ["", repo.criticalSeverityAlerts, ""] | join('') | safe %} +
{{macros.tooltip(id, mainText, "Critical severity alerts")}}
+ {% set id = [repo.name, "alerts", "high"] | join("-") %} + {% set mainText = ["", repo.highSeverityAlerts, ""] | join('') | safe %} +
{{macros.tooltip(id, mainText, "High severity alerts")}}
+ {% set id = [repo.name, "alerts", "medium"] | join("-") %} + {% set mainText = ["", repo.mediumSeverityAlerts, ""] | join('') | safe %} +
{{macros.tooltip(id, mainText, "Medium severity alerts")}}
+ {% set id = [repo.name, "alerts", "low"] | join("-") %} + {% set mainText = ["", repo.lowSeverityAlerts, ""] | join('') | safe %} +
{{macros.tooltip(id, mainText, "Low severity alerts")}}
+
+ {% else %} + Dependabot alerts have been disabled for this repository. + {% endif %} +
+

{{ repo.description }}

+ + {% if repo.topics.length %} +
+ {% for topic in repo.topics %} + {{ topic }} + {% endfor %} +
+ {% endif %} + +
+ +
+ {% if repo.dependencies.length %} +
+

+ {% if repo.dependencies.length === 1 %} + There is 1 dependency: + {% else %} + There are {{ repo.dependencies.length }} dependencies: + {% endif %} + +

+ +
+ {% else %} + + No dependencies have been discovered. +
+ Please make sure that Renovate has been configured on the repository to produce a dependency dashboard. +
+ {% endif %} +
+
+ + + {{ repo.openIssues }} + + + + + + {{ repo.openBotPrCount }} + + + + + + {{ repo.openPrCount }} + + + + + + {{ repo.updatedAt }} + + + + + + {{ repo.mostRecentPrOpenedAt }} + + + + + + {{ repo.oldestOpenPrOpenedAt }} + + + + + + {{ repo.mostRecentIssueOpenedAt }} + + + + + + {{ repo.oldestOpenIssueOpenedAt }} + + +
No repositories found.
+
+
+{% endblock %}