From 8363b6d872f3c01db1d97f871368b4243b342427 Mon Sep 17 00:00:00 2001
From: phfr24 <phfr24@inf.ufpr.br>
Date: Fri, 11 Oct 2024 10:57:42 -0300
Subject: [PATCH 01/14] upgrade dependencies

---
 package.json |   4 +-
 yarn.lock    | 138 +++++++++++++++++++++++++--------------------------
 2 files changed, 71 insertions(+), 71 deletions(-)

diff --git a/package.json b/package.json
index cac58db..8566040 100644
--- a/package.json
+++ b/package.json
@@ -9,9 +9,9 @@
     "lint": "next lint"
   },
   "dependencies": {
+    "next": "14.2.15",
     "react": "^18",
-    "react-dom": "^18",
-    "next": "14.2.13"
+    "react-dom": "^18"
   },
   "devDependencies": {
     "postcss": "^8",
diff --git a/yarn.lock b/yarn.lock
index 388879c..121ba41 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -51,55 +51,55 @@
     "@jridgewell/resolve-uri" "^3.1.0"
     "@jridgewell/sourcemap-codec" "^1.4.14"
 
-"@next/env@14.2.13":
-  version "14.2.13"
-  resolved "https://registry.yarnpkg.com/@next/env/-/env-14.2.13.tgz#ba341ba9eb70db428fc1c754f49c3c516f7bab47"
-  integrity sha512-s3lh6K8cbW1h5Nga7NNeXrbe0+2jIIYK9YaA9T7IufDWnZpozdFUp6Hf0d5rNWUKu4fEuSX2rCKlGjCrtylfDw==
-
-"@next/swc-darwin-arm64@14.2.13":
-  version "14.2.13"
-  resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.13.tgz#76f08d78360c4d27d444df7f35a56f59a48f4808"
-  integrity sha512-IkAmQEa2Htq+wHACBxOsslt+jMoV3msvxCn0WFSfJSkv/scy+i/EukBKNad36grRxywaXUYJc9mxEGkeIs8Bzg==
-
-"@next/swc-darwin-x64@14.2.13":
-  version "14.2.13"
-  resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.13.tgz#1d4821d54bb01dacc6a6c32408f8468a4f4af269"
-  integrity sha512-Dv1RBGs2TTjkwEnFMVL5XIfJEavnLqqwYSD6LXgTPdEy/u6FlSrLBSSfe1pcfqhFEXRAgVL3Wpjibe5wXJzWog==
-
-"@next/swc-linux-arm64-gnu@14.2.13":
-  version "14.2.13"
-  resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.13.tgz#79d9af8d3408df9990c8911889eca1ca6a308f19"
-  integrity sha512-yB1tYEFFqo4ZNWkwrJultbsw7NPAAxlPXURXioRl9SdW6aIefOLS+0TEsKrWBtbJ9moTDgU3HRILL6QBQnMevg==
-
-"@next/swc-linux-arm64-musl@14.2.13":
-  version "14.2.13"
-  resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.13.tgz#b13180645865b120591db2f1e831743ebc02ab36"
-  integrity sha512-v5jZ/FV/eHGoWhMKYrsAweQ7CWb8xsWGM/8m1mwwZQ/sutJjoFaXchwK4pX8NqwImILEvQmZWyb8pPTcP7htWg==
-
-"@next/swc-linux-x64-gnu@14.2.13":
-  version "14.2.13"
-  resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.13.tgz#8cb8480dfeee512648e4e08c2095aac0461b876f"
-  integrity sha512-aVc7m4YL7ViiRv7SOXK3RplXzOEe/qQzRA5R2vpXboHABs3w8vtFslGTz+5tKiQzWUmTmBNVW0UQdhkKRORmGA==
-
-"@next/swc-linux-x64-musl@14.2.13":
-  version "14.2.13"
-  resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.13.tgz#df5ca922fa1e1ee81b15a06a2d3d3ace0efd2bd7"
-  integrity sha512-4wWY7/OsSaJOOKvMsu1Teylku7vKyTuocvDLTZQq0TYv9OjiYYWt63PiE1nTuZnqQ4RPvME7Xai+9enoiN0Wrg==
-
-"@next/swc-win32-arm64-msvc@14.2.13":
-  version "14.2.13"
-  resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.13.tgz#8a7db6e71f526212587975f743b28e4d1cb829d1"
-  integrity sha512-uP1XkqCqV2NVH9+g2sC7qIw+w2tRbcMiXFEbMihkQ8B1+V6m28sshBwAB0SDmOe0u44ne1vFU66+gx/28RsBVQ==
-
-"@next/swc-win32-ia32-msvc@14.2.13":
-  version "14.2.13"
-  resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.13.tgz#6aa664f36f2d70c5ae6ffcbbc56784d33f24522d"
-  integrity sha512-V26ezyjPqQpDBV4lcWIh8B/QICQ4v+M5Bo9ykLN+sqeKKBxJVDpEc6biDVyluTXTC40f5IqCU0ttth7Es2ZuMw==
-
-"@next/swc-win32-x64-msvc@14.2.13":
-  version "14.2.13"
-  resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.13.tgz#5a920eea82a58affa6146192586716cec6c87fed"
-  integrity sha512-WwzOEAFBGhlDHE5Z73mNU8CO8mqMNLqaG+AO9ETmzdCQlJhVtWZnOl2+rqgVQS+YHunjOWptdFmNfbpwcUuEsw==
+"@next/env@14.2.15":
+  version "14.2.15"
+  resolved "https://registry.yarnpkg.com/@next/env/-/env-14.2.15.tgz#06d984e37e670d93ddd6790af1844aeb935f332f"
+  integrity sha512-S1qaj25Wru2dUpcIZMjxeMVSwkt8BK4dmWHHiBuRstcIyOsMapqT4A4jSB6onvqeygkSSmOkyny9VVx8JIGamQ==
+
+"@next/swc-darwin-arm64@14.2.15":
+  version "14.2.15"
+  resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.15.tgz#6386d585f39a1c490c60b72b1f76612ba4434347"
+  integrity sha512-Rvh7KU9hOUBnZ9TJ28n2Oa7dD9cvDBKua9IKx7cfQQ0GoYUwg9ig31O2oMwH3wm+pE3IkAQ67ZobPfEgurPZIA==
+
+"@next/swc-darwin-x64@14.2.15":
+  version "14.2.15"
+  resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.15.tgz#b7baeedc6a28f7545ad2bc55adbab25f7b45cb89"
+  integrity sha512-5TGyjFcf8ampZP3e+FyCax5zFVHi+Oe7sZyaKOngsqyaNEpOgkKB3sqmymkZfowy3ufGA/tUgDPPxpQx931lHg==
+
+"@next/swc-linux-arm64-gnu@14.2.15":
+  version "14.2.15"
+  resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.15.tgz#fa13c59d3222f70fb4cb3544ac750db2c6e34d02"
+  integrity sha512-3Bwv4oc08ONiQ3FiOLKT72Q+ndEMyLNsc/D3qnLMbtUYTQAmkx9E/JRu0DBpHxNddBmNT5hxz1mYBphJ3mfrrw==
+
+"@next/swc-linux-arm64-musl@14.2.15":
+  version "14.2.15"
+  resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.15.tgz#30e45b71831d9a6d6d18d7ac7d611a8d646a17f9"
+  integrity sha512-k5xf/tg1FBv/M4CMd8S+JL3uV9BnnRmoe7F+GWC3DxkTCD9aewFRH1s5rJ1zkzDa+Do4zyN8qD0N8c84Hu96FQ==
+
+"@next/swc-linux-x64-gnu@14.2.15":
+  version "14.2.15"
+  resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.15.tgz#5065db17fc86f935ad117483f21f812dc1b39254"
+  integrity sha512-kE6q38hbrRbKEkkVn62reLXhThLRh6/TvgSP56GkFNhU22TbIrQDEMrO7j0IcQHcew2wfykq8lZyHFabz0oBrA==
+
+"@next/swc-linux-x64-musl@14.2.15":
+  version "14.2.15"
+  resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.15.tgz#3c4a4568d8be7373a820f7576cf33388b5dab47e"
+  integrity sha512-PZ5YE9ouy/IdO7QVJeIcyLn/Rc4ml9M2G4y3kCM9MNf1YKvFY4heg3pVa/jQbMro+tP6yc4G2o9LjAz1zxD7tQ==
+
+"@next/swc-win32-arm64-msvc@14.2.15":
+  version "14.2.15"
+  resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.15.tgz#fb812cc4ca0042868e32a6a021da91943bb08b98"
+  integrity sha512-2raR16703kBvYEQD9HNLyb0/394yfqzmIeyp2nDzcPV4yPjqNUG3ohX6jX00WryXz6s1FXpVhsCo3i+g4RUX+g==
+
+"@next/swc-win32-ia32-msvc@14.2.15":
+  version "14.2.15"
+  resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.15.tgz#ec26e6169354f8ced240c1427be7fd485c5df898"
+  integrity sha512-fyTE8cklgkyR1p03kJa5zXEaZ9El+kDNM5A+66+8evQS5e/6v0Gk28LqA0Jet8gKSOyP+OTm/tJHzMlGdQerdQ==
+
+"@next/swc-win32-x64-msvc@14.2.15":
+  version "14.2.15"
+  resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.15.tgz#18d68697002b282006771f8d92d79ade9efd35c4"
+  integrity sha512-SzqGbsLsP9OwKNUG9nekShTwhj6JSB9ZLMWQ8g1gG6hdE5gQLncbnbymrwy2yVmH9nikSLYRYxYMFu78Ggp7/g==
 
 "@nodelib/fs.scandir@2.1.5":
   version "2.1.5"
@@ -217,9 +217,9 @@ camelcase-css@^2.0.1:
   integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==
 
 caniuse-lite@^1.0.30001579:
-  version "1.0.30001663"
-  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001663.tgz#1529a723505e429fdfd49532e9fc42273ba7fed7"
-  integrity sha512-o9C3X27GLKbLeTYZ6HBOLU1tsAcBZsLis28wrVzddShCS16RujjHp9GDHKZqrB3meE0YjhawvMFsGb/igqiPzA==
+  version "1.0.30001668"
+  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001668.tgz#98e214455329f54bf7a4d70b49c9794f0fbedbed"
+  integrity sha512-nWLrdxqCdblixUO+27JtGJJE/txpJlyUy5YN1u53wLZkP0emYCo5zgS6QYft7VUYR42LGgi/S5hdLZTrnyIddw==
 
 chokidar@^3.5.3:
   version "3.6.0"
@@ -504,12 +504,12 @@ nanoid@^3.3.6, nanoid@^3.3.7:
   resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8"
   integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==
 
-next@14.2.13:
-  version "14.2.13"
-  resolved "https://registry.yarnpkg.com/next/-/next-14.2.13.tgz#32da2ee0afbe729e2d4a467c3570def90e1c974d"
-  integrity sha512-BseY9YNw8QJSwLYD7hlZzl6QVDoSFHL/URN5K64kVEVpCsSOWeyjbIGK+dZUaRViHTaMQX8aqmnn0PHBbGZezg==
+next@14.2.15:
+  version "14.2.15"
+  resolved "https://registry.yarnpkg.com/next/-/next-14.2.15.tgz#348e5603e22649775d19c785c09a89c9acb5189a"
+  integrity sha512-h9ctmOokpoDphRvMGnwOJAedT6zKhwqyZML9mDtspgf4Rh3Pn7UTYKqePNoDvhsWBAO5GoPNYshnAUGIazVGmw==
   dependencies:
-    "@next/env" "14.2.13"
+    "@next/env" "14.2.15"
     "@swc/helpers" "0.5.5"
     busboy "1.6.0"
     caniuse-lite "^1.0.30001579"
@@ -517,15 +517,15 @@ next@14.2.13:
     postcss "8.4.31"
     styled-jsx "5.1.1"
   optionalDependencies:
-    "@next/swc-darwin-arm64" "14.2.13"
-    "@next/swc-darwin-x64" "14.2.13"
-    "@next/swc-linux-arm64-gnu" "14.2.13"
-    "@next/swc-linux-arm64-musl" "14.2.13"
-    "@next/swc-linux-x64-gnu" "14.2.13"
-    "@next/swc-linux-x64-musl" "14.2.13"
-    "@next/swc-win32-arm64-msvc" "14.2.13"
-    "@next/swc-win32-ia32-msvc" "14.2.13"
-    "@next/swc-win32-x64-msvc" "14.2.13"
+    "@next/swc-darwin-arm64" "14.2.15"
+    "@next/swc-darwin-x64" "14.2.15"
+    "@next/swc-linux-arm64-gnu" "14.2.15"
+    "@next/swc-linux-arm64-musl" "14.2.15"
+    "@next/swc-linux-x64-gnu" "14.2.15"
+    "@next/swc-linux-x64-musl" "14.2.15"
+    "@next/swc-win32-arm64-msvc" "14.2.15"
+    "@next/swc-win32-ia32-msvc" "14.2.15"
+    "@next/swc-win32-x64-msvc" "14.2.15"
 
 normalize-path@^3.0.0, normalize-path@~3.0.0:
   version "3.0.0"
@@ -543,9 +543,9 @@ object-hash@^3.0.0:
   integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==
 
 package-json-from-dist@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz#e501cd3094b278495eb4258d4c9f6d5ac3019f00"
-  integrity sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505"
+  integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==
 
 path-key@^3.1.0:
   version "3.1.1"
-- 
GitLab


From 46804c7c0cc9a263739e64c178a94cffbe8ebbc4 Mon Sep 17 00:00:00 2001
From: phfr24 <phfr24@inf.ufpr.br>
Date: Fri, 11 Oct 2024 10:59:32 -0300
Subject: [PATCH 02/14] add ws

---
 package.json | 3 ++-
 yarn.lock    | 5 +++++
 2 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/package.json b/package.json
index 8566040..cffa4de 100644
--- a/package.json
+++ b/package.json
@@ -11,7 +11,8 @@
   "dependencies": {
     "next": "14.2.15",
     "react": "^18",
-    "react-dom": "^18"
+    "react-dom": "^18",
+    "ws": "^8.18.0"
   },
   "devDependencies": {
     "postcss": "^8",
diff --git a/yarn.lock b/yarn.lock
index 121ba41..651400d 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -898,6 +898,11 @@ wrap-ansi@^8.1.0:
     string-width "^5.0.1"
     strip-ansi "^7.0.1"
 
+ws@^8.18.0:
+  version "8.18.0"
+  resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc"
+  integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==
+
 yaml@^2.3.4:
   version "2.5.1"
   resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.5.1.tgz#c9772aacf62cb7494a95b0c4f1fb065b563db130"
-- 
GitLab


From 9a33e4413e6afb45d0617620cdb29dfa8a532b31 Mon Sep 17 00:00:00 2001
From: phfr24 <phfr24@inf.ufpr.br>
Date: Fri, 11 Oct 2024 17:24:57 -0300
Subject: [PATCH 03/14] integrate websocket into /analysis

---
 src/app/analysis/[id]/page.js | 64 ++++++++++++++++++++++++++---------
 1 file changed, 48 insertions(+), 16 deletions(-)

diff --git a/src/app/analysis/[id]/page.js b/src/app/analysis/[id]/page.js
index 7058e3f..fb874dd 100644
--- a/src/app/analysis/[id]/page.js
+++ b/src/app/analysis/[id]/page.js
@@ -1,33 +1,65 @@
+'use client'
 import { AnalysisMetadata, SampleMetadata } from "@/components/analysis/metadata";
-import ActivityTimeline from "@/components/analysis/activity-timeline";
 import RedirectButton from "@/components/redirect-button";
+import { useState, useEffect } from 'react';
+import "@/styles/analysis/activity-timeline.css";
 import "@/styles/analysis/page.css";
 
 export default function Analysis({ params: { id } }) {
+  const [report, setReport] = useState([]);
+
+  useEffect(() => {
+    const ws = new WebSocket(`ws://localhost:8080/status/${id}`);
+
+    ws.onopen = (event) => {
+      console.log('Connection opened: ', event);
+    }
+    ws.onerror = (error) => {
+      console.error('Error: ', error);
+    }
+    ws.onmessage = (event) => {
+      setReport(JSON.parse(event.data));
+    }
+    ws.onclose = (event) => {
+      console.log('Connection closed: ', event);
+    }
+
+    return () => {
+      ws.close();
+    }
+  }, [id, setReport, report]);
+
   return (
     <div>
       <div className="pagerow">
         <AnalysisMetadata
-          status="running"
-          id="5e6c83bf-44b3-4f9e-bd45-683585439eed"
-          driver="1.3.8"
-          template="9011"
-          start="08/10/24 11:00:10 -03"
-          end="08/10/24 11:07:39 -03"
+          status={report?.status || ''}
+          id={report?.id || ''}
+          driver={report?.driverVersion || ''}
+          template={report?.templateID || ''}
+          start={report?.startTime || ''}
+          end={report?.endTime || ''}
         />
         <SampleMetadata
-          filename="MALWARE2.exe"
-          extension=".exe"
-          mimetype="application/octet-stream"
-          size="2.7 MiB"
-          lastmod="08/10/24 11:00:10 -03"
-          md5="6cb20b4c787c4ea918e301310b2667f5"
-          sha1="5b5d921d69336d06837422438cd0c5225fc78a74"
-          sha256="83121822f08691834102f7652c673133deb98a134110a0fea22a27b2ccd5d966"
+          filename={report?.fileMetadata?.filename || ''}
+          extension={report?.fileMetadata?.extension || ''}
+          mimetype={report?.fileMetadata?.mimetype || ''}
+          size={report?.fileMetadata?.size || ''}
+          lastmod={report?.fileMetadata?.lastModified || ''}
+          md5={report?.fileMetadata?.md5sum || ''}
+          sha1={report?.fileMetadata?.sha1sum || ''}
+          sha256={report?.fileMetadata?.sha256sum || ''}
         />
       </div>
       <div className="pagerow">
-        <ActivityTimeline />
+        <div className="activity-timeline">
+          <div className="at-title">ACTIVITY TIMELINE</div>
+          {() => {
+            if (report) {
+              return report.log.map((msg, i) => <div key={i}>{msg}</div>);
+            }
+          }}
+        </div>
       </div>
       <RedirectButton name="go back" href="/" />
     </div>
-- 
GitLab


From b1f0f28f8e947f990a8b6424b406e20555395442 Mon Sep 17 00:00:00 2001
From: phfr24 <phfr24@inf.ufpr.br>
Date: Fri, 11 Oct 2024 20:06:57 -0300
Subject: [PATCH 04/14] update /analysis page.js

---
 src/app/analysis/[id]/page.js | 81 +++++++++++++++++++----------------
 1 file changed, 45 insertions(+), 36 deletions(-)

diff --git a/src/app/analysis/[id]/page.js b/src/app/analysis/[id]/page.js
index fb874dd..01939d0 100644
--- a/src/app/analysis/[id]/page.js
+++ b/src/app/analysis/[id]/page.js
@@ -6,62 +6,71 @@ import "@/styles/analysis/activity-timeline.css";
 import "@/styles/analysis/page.css";
 
 export default function Analysis({ params: { id } }) {
-  const [report, setReport] = useState([]);
+  const [report, setReport] = useState(null);
+
+  /* TODO: check if it is possible to make the ws url configurable.
+  /* TODO: check id against http://backend/analysis/id. if it returns 404, then
+   * 404. */
+  /* TODO: use report.error !!! */
 
   useEffect(() => {
     const ws = new WebSocket(`ws://localhost:8080/status/${id}`);
 
     ws.onopen = (event) => {
-      console.log('Connection opened: ', event);
+      //console.log('websocket connection opened: ', event);
     }
     ws.onerror = (error) => {
-      console.error('Error: ', error);
+      //console.error('websocket connection error: ', error);
     }
     ws.onmessage = (event) => {
       setReport(JSON.parse(event.data));
     }
     ws.onclose = (event) => {
-      console.log('Connection closed: ', event);
+      //console.log('websocket connection closed: ', event);
     }
 
     return () => {
       ws.close();
     }
-  }, [id, setReport, report]);
+  }, [id, setReport]);
 
   return (
     <div>
-      <div className="pagerow">
-        <AnalysisMetadata
-          status={report?.status || ''}
-          id={report?.id || ''}
-          driver={report?.driverVersion || ''}
-          template={report?.templateID || ''}
-          start={report?.startTime || ''}
-          end={report?.endTime || ''}
-        />
-        <SampleMetadata
-          filename={report?.fileMetadata?.filename || ''}
-          extension={report?.fileMetadata?.extension || ''}
-          mimetype={report?.fileMetadata?.mimetype || ''}
-          size={report?.fileMetadata?.size || ''}
-          lastmod={report?.fileMetadata?.lastModified || ''}
-          md5={report?.fileMetadata?.md5sum || ''}
-          sha1={report?.fileMetadata?.sha1sum || ''}
-          sha256={report?.fileMetadata?.sha256sum || ''}
-        />
-      </div>
-      <div className="pagerow">
-        <div className="activity-timeline">
-          <div className="at-title">ACTIVITY TIMELINE</div>
-          {() => {
-            if (report) {
-              return report.log.map((msg, i) => <div key={i}>{msg}</div>);
-            }
-          }}
-        </div>
-      </div>
-      <RedirectButton name="go back" href="/" />
+      {if (report) {
+        return (
+          <div className="pagerow">
+            <AnalysisMetadata
+              status={report.status}
+              id={report.id}
+              driver={report.driverVersion}
+              template={report.templateID}
+              start={report.startTime}
+              end={report.endTime}
+            />
+            <SampleMetadata
+              filename={report.fileMetadata.filename}
+              extension={report.fileMetadata.extension}
+              mimetype={report.fileMetadata.mimetype}
+              size={report.fileMetadata.size}
+              lastmod={report.fileMetadata.lastModified}
+              md5={report.fileMetadata.md5sum}
+              sha1={report.fileMetadata.sha1sum}
+              sha256={report.fileMetadata.sha256sum}
+            />
+          </div>
+          <div className="pagerow">
+            <div className="activity-timeline">
+              <div className="at-title">ACTIVITY TIMELINE</div>
+              {() => {
+                if (report) {
+                  return report.log.map((msg, i) => <div key={i}>{msg}</div>);
+                }
+              }}
+            </div>
+          </div>
+          <RedirectButton name="go back" href="/" />
+        );
+      }}
     </div>
   );
 }
-- 
GitLab


From c52765533dc5842e8d108170b91416937e7a3ffa Mon Sep 17 00:00:00 2001
From: phfr24 <phfr24@inf.ufpr.br>
Date: Mon, 14 Oct 2024 10:10:38 -0300
Subject: [PATCH 05/14] update /analysis page.js

---
 src/app/analysis/[id]/page.js | 20 ++++++++------------
 1 file changed, 8 insertions(+), 12 deletions(-)

diff --git a/src/app/analysis/[id]/page.js b/src/app/analysis/[id]/page.js
index 01939d0..4304aaf 100644
--- a/src/app/analysis/[id]/page.js
+++ b/src/app/analysis/[id]/page.js
@@ -35,9 +35,9 @@ export default function Analysis({ params: { id } }) {
   }, [id, setReport]);
 
   return (
-    <div>
-      {if (report) {
-        return (
+    <main>
+      {report && (
+        <div>
           <div className="pagerow">
             <AnalysisMetadata
               status={report.status}
@@ -61,16 +61,12 @@ export default function Analysis({ params: { id } }) {
           <div className="pagerow">
             <div className="activity-timeline">
               <div className="at-title">ACTIVITY TIMELINE</div>
-              {() => {
-                if (report) {
-                  return report.log.map((msg, i) => <div key={i}>{msg}</div>);
-                }
-              }}
+              {report.log.map((msg, i) => <div key={i}>{msg}</div>)}
             </div>
           </div>
-          <RedirectButton name="go back" href="/" />
-        );
-      }}
-    </div>
+          <RedirectButton name="go back" href="/" /> 
+        </div>
+      )}
+    </main>
   );
 }
-- 
GitLab


From b0f60bab4fca4023a3e1cc5868f1833f4ad4791e Mon Sep 17 00:00:00 2001
From: phfr24 <phfr24@inf.ufpr.br>
Date: Mon, 14 Oct 2024 10:21:32 -0300
Subject: [PATCH 06/14] update activity timeline spacing

---
 src/styles/analysis/activity-timeline.css | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/src/styles/analysis/activity-timeline.css b/src/styles/analysis/activity-timeline.css
index 6d481f7..9d422b2 100644
--- a/src/styles/analysis/activity-timeline.css
+++ b/src/styles/analysis/activity-timeline.css
@@ -7,7 +7,7 @@
   font-size: 1em;
   flex: 1;
   overflow: auto;
-  padding: 0.5em;
+  padding: 0.5em 0.5em 0 0.5em;
   word-wrap: break-word;
   white-space: pre-wrap;
   border: 1px solid #1F1C1A;
@@ -21,3 +21,7 @@
   text-align: center;
   border-bottom: 1px solid #1F1C1A;
 }
+
+.activity-timeline div {
+  margin-bottom: 0.5em;
+}
\ No newline at end of file
-- 
GitLab


From 397f29faaa8305062e5cd33fef156f7be49c912e Mon Sep 17 00:00:00 2001
From: phfr24 <phfr24@inf.ufpr.br>
Date: Mon, 14 Oct 2024 10:33:06 -0300
Subject: [PATCH 07/14] format file size in /analysis

---
 src/app/analysis/[id]/page.js | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/src/app/analysis/[id]/page.js b/src/app/analysis/[id]/page.js
index 4304aaf..49cb117 100644
--- a/src/app/analysis/[id]/page.js
+++ b/src/app/analysis/[id]/page.js
@@ -51,7 +51,7 @@ export default function Analysis({ params: { id } }) {
               filename={report.fileMetadata.filename}
               extension={report.fileMetadata.extension}
               mimetype={report.fileMetadata.mimetype}
-              size={report.fileMetadata.size}
+              size={formatFileSize(report.fileMetadata.size)}
               lastmod={report.fileMetadata.lastModified}
               md5={report.fileMetadata.md5sum}
               sha1={report.fileMetadata.sha1sum}
@@ -70,3 +70,13 @@ export default function Analysis({ params: { id } }) {
     </main>
   );
 }
+
+function formatFileSize(bytes) {
+  const sizes = ['B', 'KB', 'MB', 'GB'];
+  
+  if (bytes === 0)
+    return '0B';
+
+  const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
+  return `${(bytes / Math.pow(1024, i)).toFixed(2)} ${sizes[i]}`;
+}
\ No newline at end of file
-- 
GitLab


From 7995a09ae72f06bf111a7519c47aba5ba3337870 Mon Sep 17 00:00:00 2001
From: phfr24 <phfr24@inf.ufpr.br>
Date: Mon, 14 Oct 2024 11:03:53 -0300
Subject: [PATCH 08/14] document project configuration

---
 README.md                     | 50 +++++++++++++++++++----------------
 next.config.mjs               | 11 +++++++-
 src/app/analysis/[id]/page.js |  1 -
 3 files changed, 37 insertions(+), 25 deletions(-)

diff --git a/README.md b/README.md
index c474cb2..45f620d 100644
--- a/README.md
+++ b/README.md
@@ -1,24 +1,28 @@
-This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
-
-## Getting Started
-
-First, run the development server:
-
-```bash
-yarn dev
+## Configuration
+
+This sucks, but we'll come up with something better later.
+
+1. Set the API url in the `next.config.mjs` file:
+```js
+/* next.config.mjs */
+const nextConfig = {
+  // ...
+  async rewrites() {
+    return [
+      {
+        source: "/api/:path*",
+        destination: "http{s}://{API URL}/:path*",
+      },
+    ];
+  },
+};
 ```
-
-Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
-
-You can start editing the page by modifying `app/page.js`. The page auto-updates as you edit the file.
-
-This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
-
-## Learn More
-
-To learn more about Next.js, take a look at the following resources:
-
-- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
-- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
-
-You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
+2. Set the websocket url in the `src/app/analysis/[id]/page.js` file:
+```js
+/* src/app/analysis/[id]/page.js */
+export default function Analysis({ params: {id} }) {
+  // ...
+  useEffect(() => {
+    const ws = new WebSocket(`ws{s}://{WEBSOCKET URL}/${id}`);
+    // ...
+```
\ No newline at end of file
diff --git a/next.config.mjs b/next.config.mjs
index 4678774..48f0bf7 100644
--- a/next.config.mjs
+++ b/next.config.mjs
@@ -1,4 +1,13 @@
 /** @type {import('next').NextConfig} */
-const nextConfig = {};
+const nextConfig = {
+  async rewrites() {
+    return [
+      {
+        source: "/api/:path*",
+        destination: "http://localhost:8080/:path*",
+      },
+    ];
+  },
+};
 
 export default nextConfig;
diff --git a/src/app/analysis/[id]/page.js b/src/app/analysis/[id]/page.js
index 49cb117..152e940 100644
--- a/src/app/analysis/[id]/page.js
+++ b/src/app/analysis/[id]/page.js
@@ -8,7 +8,6 @@ import "@/styles/analysis/page.css";
 export default function Analysis({ params: { id } }) {
   const [report, setReport] = useState(null);
 
-  /* TODO: check if it is possible to make the ws url configurable.
   /* TODO: check id against http://backend/analysis/id. if it returns 404, then
    * 404. */
   /* TODO: use report.error !!! */
-- 
GitLab


From 3acd85f3a89dce79c34d167c62ddcd0188d03b2a Mon Sep 17 00:00:00 2001
From: phfr24 <phfr24@inf.ufpr.br>
Date: Mon, 14 Oct 2024 11:08:04 -0300
Subject: [PATCH 09/14] update readme

---
 README.md | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/README.md b/README.md
index 45f620d..2024614 100644
--- a/README.md
+++ b/README.md
@@ -25,4 +25,19 @@ export default function Analysis({ params: {id} }) {
   useEffect(() => {
     const ws = new WebSocket(`ws{s}://{WEBSOCKET URL}/${id}`);
     // ...
+```
+
+## Development
+
+1. Enable yarn:
+```sh
+corepack enable
+```
+2. Install dependencies:
+```sh
+yarn install
+```
+3. Start the development server:
+```sh
+yarn dev
 ```
\ No newline at end of file
-- 
GitLab


From 5c4cb23408b0c36bd2113c101f0a9899c7d60c40 Mon Sep 17 00:00:00 2001
From: phfr24 <phfr24@inf.ufpr.br>
Date: Mon, 14 Oct 2024 15:12:28 -0300
Subject: [PATCH 10/14] fernando

---
 src/app/analysis/[id]/page.js | 15 +++++++++++++--
 1 file changed, 13 insertions(+), 2 deletions(-)

diff --git a/src/app/analysis/[id]/page.js b/src/app/analysis/[id]/page.js
index 152e940..af1ad71 100644
--- a/src/app/analysis/[id]/page.js
+++ b/src/app/analysis/[id]/page.js
@@ -4,15 +4,26 @@ import RedirectButton from "@/components/redirect-button";
 import { useState, useEffect } from 'react';
 import "@/styles/analysis/activity-timeline.css";
 import "@/styles/analysis/page.css";
+import { notFound } from "next/navigation";
 
 export default function Analysis({ params: { id } }) {
   const [report, setReport] = useState(null);
+  const [failed, setFailed] = useState(false);
 
   /* TODO: check id against http://backend/analysis/id. if it returns 404, then
    * 404. */
   /* TODO: use report.error !!! */
+  fetch(`/api/analysis/${id}`).then((res) => {
+    console.log(res);
+    setFailed(!res.ok);
+  });
 
   useEffect(() => {
+    if (failed) { notFound(); }
+  }, [failed]);
+
+  useEffect(() => {
+
     const ws = new WebSocket(`ws://localhost:8080/status/${id}`);
 
     ws.onopen = (event) => {
@@ -63,7 +74,7 @@ export default function Analysis({ params: { id } }) {
               {report.log.map((msg, i) => <div key={i}>{msg}</div>)}
             </div>
           </div>
-          <RedirectButton name="go back" href="/" /> 
+          <RedirectButton name="go back" href="/" />
         </div>
       )}
     </main>
@@ -72,7 +83,7 @@ export default function Analysis({ params: { id } }) {
 
 function formatFileSize(bytes) {
   const sizes = ['B', 'KB', 'MB', 'GB'];
-  
+
   if (bytes === 0)
     return '0B';
 
-- 
GitLab


From 0b4814d8b6ff9f33b7ddfb463e3e74617d417d83 Mon Sep 17 00:00:00 2001
From: pedro friedrich <phfr24@inf.ufpr.br>
Date: Mon, 14 Oct 2024 17:06:30 -0300
Subject: [PATCH 11/14] fix insanity

---
 src/app/analysis/[id]/page.js | 44 +++++++++++++++++++----------------
 1 file changed, 24 insertions(+), 20 deletions(-)

diff --git a/src/app/analysis/[id]/page.js b/src/app/analysis/[id]/page.js
index af1ad71..8e21a41 100644
--- a/src/app/analysis/[id]/page.js
+++ b/src/app/analysis/[id]/page.js
@@ -2,42 +2,32 @@
 import { AnalysisMetadata, SampleMetadata } from "@/components/analysis/metadata";
 import RedirectButton from "@/components/redirect-button";
 import { useState, useEffect } from 'react';
+import { notFound } from "next/navigation";
 import "@/styles/analysis/activity-timeline.css";
 import "@/styles/analysis/page.css";
-import { notFound } from "next/navigation";
+
+/* TODO: use report.error !!! */
+
+const api_host = "localhost:8080";
 
 export default function Analysis({ params: { id } }) {
   const [report, setReport] = useState(null);
   const [failed, setFailed] = useState(false);
 
-  /* TODO: check id against http://backend/analysis/id. if it returns 404, then
-   * 404. */
-  /* TODO: use report.error !!! */
-  fetch(`/api/analysis/${id}`).then((res) => {
-    console.log(res);
-    setFailed(!res.ok);
-  });
+  validateId(id, setFailed);
 
   useEffect(() => {
-    if (failed) { notFound(); }
+    if (failed) {
+      notFound();
+    }
   }, [failed]);
 
   useEffect(() => {
+    const ws = new WebSocket(`ws://${api_host}/status/${id}`);
 
-    const ws = new WebSocket(`ws://localhost:8080/status/${id}`);
-
-    ws.onopen = (event) => {
-      //console.log('websocket connection opened: ', event);
-    }
-    ws.onerror = (error) => {
-      //console.error('websocket connection error: ', error);
-    }
     ws.onmessage = (event) => {
       setReport(JSON.parse(event.data));
     }
-    ws.onclose = (event) => {
-      //console.log('websocket connection closed: ', event);
-    }
 
     return () => {
       ws.close();
@@ -81,6 +71,20 @@ export default function Analysis({ params: { id } }) {
   );
 }
 
+function validateId(id, setFailed) {
+  fetch(`https://${api_host}/analysis/${id}`, { cache: 'no-store' })
+    .then((res) => {
+      if (!res.ok) {
+        throw new Error(`Failed to validate id: got ${res.status} response from api`);
+      }
+      setFailed(false);
+    })
+    .catch((err) => {
+      //console.error('fetch()', err);
+      setFailed(true);
+    });
+}
+
 function formatFileSize(bytes) {
   const sizes = ['B', 'KB', 'MB', 'GB'];
 
-- 
GitLab


From 4bdb357e8321816627f8f25c9d4a3d7b6b55ac1d Mon Sep 17 00:00:00 2001
From: pedro friedrich <phfr24@inf.ufpr.br>
Date: Mon, 14 Oct 2024 17:12:36 -0300
Subject: [PATCH 12/14] display report error

---
 src/app/analysis/[id]/page.js             | 3 +--
 src/styles/analysis/activity-timeline.css | 5 +++++
 2 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/src/app/analysis/[id]/page.js b/src/app/analysis/[id]/page.js
index 8e21a41..e9d0274 100644
--- a/src/app/analysis/[id]/page.js
+++ b/src/app/analysis/[id]/page.js
@@ -6,8 +6,6 @@ import { notFound } from "next/navigation";
 import "@/styles/analysis/activity-timeline.css";
 import "@/styles/analysis/page.css";
 
-/* TODO: use report.error !!! */
-
 const api_host = "localhost:8080";
 
 export default function Analysis({ params: { id } }) {
@@ -62,6 +60,7 @@ export default function Analysis({ params: { id } }) {
             <div className="activity-timeline">
               <div className="at-title">ACTIVITY TIMELINE</div>
               {report.log.map((msg, i) => <div key={i}>{msg}</div>)}
+              {report.error && <div className="at-error">{report.error}</div>}
             </div>
           </div>
           <RedirectButton name="go back" href="/" />
diff --git a/src/styles/analysis/activity-timeline.css b/src/styles/analysis/activity-timeline.css
index 9d422b2..37633a9 100644
--- a/src/styles/analysis/activity-timeline.css
+++ b/src/styles/analysis/activity-timeline.css
@@ -24,4 +24,9 @@
 
 .activity-timeline div {
   margin-bottom: 0.5em;
+}
+
+.at-error {
+  color: #FF0000;
+  font-weight: bold;
 }
\ No newline at end of file
-- 
GitLab


From 82ab1554ab0cac1ccd6d7d237e6172584338a5c3 Mon Sep 17 00:00:00 2001
From: pedro friedrich <phfr24@inf.ufpr.br>
Date: Mon, 14 Oct 2024 17:15:40 -0300
Subject: [PATCH 13/14] update readme

---
 README.md | 13 ++++++-------
 1 file changed, 6 insertions(+), 7 deletions(-)

diff --git a/README.md b/README.md
index 2024614..b8a5c44 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
 
 This sucks, but we'll come up with something better later.
 
-1. Set the API url in the `next.config.mjs` file:
+1. Set the API host in `next.config.mjs`:
 ```js
 /* next.config.mjs */
 const nextConfig = {
@@ -11,22 +11,21 @@ const nextConfig = {
     return [
       {
         source: "/api/:path*",
-        destination: "http{s}://{API URL}/:path*",
+        destination: "http{s}://{api_host}/:path*",
       },
     ];
   },
 };
 ```
-2. Set the websocket url in the `src/app/analysis/[id]/page.js` file:
+2. Set the API host in `src/app/analysis/[id]/page.js`:
 ```js
 /* src/app/analysis/[id]/page.js */
-export default function Analysis({ params: {id} }) {
-  // ...
-  useEffect(() => {
-    const ws = new WebSocket(`ws{s}://{WEBSOCKET URL}/${id}`);
+const api_host = "{api_host}";
     // ...
 ```
 
+e.g. api_host = "localhost:8080"
+
 ## Development
 
 1. Enable yarn:
-- 
GitLab


From 338bf914f833404ef6e9d266a803e58991f02ff6 Mon Sep 17 00:00:00 2001
From: phfr24 <phfr24@inf.ufpr.br>
Date: Tue, 15 Oct 2024 11:14:32 -0300
Subject: [PATCH 14/14] make api_host configurable

---
 README.md                     | 42 ++++++++++++++++-------------------
 next.config.mjs               |  2 +-
 src/app/analysis/[id]/page.js |  6 ++---
 3 files changed, 22 insertions(+), 28 deletions(-)

diff --git a/README.md b/README.md
index b8a5c44..b65c825 100644
--- a/README.md
+++ b/README.md
@@ -1,30 +1,26 @@
 ## Configuration
 
-This sucks, but we'll come up with something better later.
+1. Create a `.env.local` file in the root directory of the project:
+```bash
+# API host for internal requests.
+#
+# For example, if the API is running on localhost:8080,
+# set INTERNAL_API_HOST=localhost:8080.
+#
+# If the API is running as a docker compose service called "api",
+# set INTERNAL_API_HOST=api:8080.
+INTERNAL_API_HOST=
 
-1. Set the API host in `next.config.mjs`:
-```js
-/* next.config.mjs */
-const nextConfig = {
-  // ...
-  async rewrites() {
-    return [
-      {
-        source: "/api/:path*",
-        destination: "http{s}://{api_host}/:path*",
-      },
-    ];
-  },
-};
+# API host for public requests.
+#
+# This is the host that the client will use to make requests to the API.
+# It is needed because the client connects to the API from the browser
+# for websocket connections.
+#
+# For example, if the API is running on saci.inf.ufpr.br:8080,
+# set NEXT_PUBLIC_API_HOST=saci.inf.ufpr.br:8080.
+NEXT_PUBLIC_API_HOST=
 ```
-2. Set the API host in `src/app/analysis/[id]/page.js`:
-```js
-/* src/app/analysis/[id]/page.js */
-const api_host = "{api_host}";
-    // ...
-```
-
-e.g. api_host = "localhost:8080"
 
 ## Development
 
diff --git a/next.config.mjs b/next.config.mjs
index 48f0bf7..be0bab0 100644
--- a/next.config.mjs
+++ b/next.config.mjs
@@ -4,7 +4,7 @@ const nextConfig = {
     return [
       {
         source: "/api/:path*",
-        destination: "http://localhost:8080/:path*",
+        destination: `http://${process.env.INTERNAL_API_HOST}/:path*`,
       },
     ];
   },
diff --git a/src/app/analysis/[id]/page.js b/src/app/analysis/[id]/page.js
index e9d0274..4dc844d 100644
--- a/src/app/analysis/[id]/page.js
+++ b/src/app/analysis/[id]/page.js
@@ -6,8 +6,6 @@ import { notFound } from "next/navigation";
 import "@/styles/analysis/activity-timeline.css";
 import "@/styles/analysis/page.css";
 
-const api_host = "localhost:8080";
-
 export default function Analysis({ params: { id } }) {
   const [report, setReport] = useState(null);
   const [failed, setFailed] = useState(false);
@@ -21,7 +19,7 @@ export default function Analysis({ params: { id } }) {
   }, [failed]);
 
   useEffect(() => {
-    const ws = new WebSocket(`ws://${api_host}/status/${id}`);
+    const ws = new WebSocket(`ws://${process.env.NEXT_PUBLIC_API_HOST}/status/${id}`);
 
     ws.onmessage = (event) => {
       setReport(JSON.parse(event.data));
@@ -71,7 +69,7 @@ export default function Analysis({ params: { id } }) {
 }
 
 function validateId(id, setFailed) {
-  fetch(`https://${api_host}/analysis/${id}`, { cache: 'no-store' })
+  fetch(`http://${process.env.NEXT_PUBLIC_API_HOST}/analysis/${id}`, { cache: 'no-store' })
     .then((res) => {
       if (!res.ok) {
         throw new Error(`Failed to validate id: got ${res.status} response from api`);
-- 
GitLab