diff --git a/package.json b/package.json
index f72cb5f..036806e 100644
--- a/package.json
+++ b/package.json
@@ -36,5 +36,19 @@
"lint-staged": {
"*.{js,jsx}": "eslint --cache --fix",
"*.{js,css,md}": "prettier --write"
+ },
+ "pkg": {
+ "assets": [
+ "./node_modules/**/*.node",
+ "./packages/**/.babel/*.env",
+ "./packages/server/.babel/static/",
+ "./packages/desktop/node_modules/active-win/main"
+ ],
+ "targets": [
+ "node14-win-x64",
+ "node14-linux-x64",
+ "node14-macos"
+ ],
+ "outputPath": "build"
}
}
diff --git a/packages/Activity-Tracker-browser-extension/images/icon128.png b/packages/Activity-Tracker-browser-extension/images/icon128.png
new file mode 100644
index 0000000..8ba8a61
Binary files /dev/null and b/packages/Activity-Tracker-browser-extension/images/icon128.png differ
diff --git a/packages/Activity-Tracker-browser-extension/images/icon16.png b/packages/Activity-Tracker-browser-extension/images/icon16.png
new file mode 100644
index 0000000..4994be1
Binary files /dev/null and b/packages/Activity-Tracker-browser-extension/images/icon16.png differ
diff --git a/packages/Activity-Tracker-browser-extension/images/icon32.png b/packages/Activity-Tracker-browser-extension/images/icon32.png
new file mode 100644
index 0000000..b919666
Binary files /dev/null and b/packages/Activity-Tracker-browser-extension/images/icon32.png differ
diff --git a/packages/Activity-Tracker-browser-extension/images/icon48.png b/packages/Activity-Tracker-browser-extension/images/icon48.png
new file mode 100644
index 0000000..05d7b49
Binary files /dev/null and b/packages/Activity-Tracker-browser-extension/images/icon48.png differ
diff --git a/packages/Activity-Tracker-browser-extension/manifest.json b/packages/Activity-Tracker-browser-extension/manifest.json
new file mode 100644
index 0000000..c102c01
--- /dev/null
+++ b/packages/Activity-Tracker-browser-extension/manifest.json
@@ -0,0 +1,26 @@
+{
+ "name": "Browser Activity Tracker",
+ "description": "Shows you your digital time based on your browser activity",
+ "version": "1.0.0",
+ "manifest_version": 3,
+
+ "action": {
+ "default_icon": {
+ "16": "images/icon16.png",
+ "32": "images/icon32.png",
+ "48": "images/icon48.png",
+ "128": "images/icon128.png"
+ },
+ "default_popup": "popup.html"
+ },
+
+ "background": {
+ "service_worker": "src/background.js",
+ "type": "module"
+ },
+
+ "author": "",
+ "homepage_url": "https://github.com/OpenLake/Activity-Tracker#readme",
+
+ "permissions": ["tabs", "activeTab", "storage"]
+}
diff --git a/packages/Activity-Tracker-browser-extension/popup.html b/packages/Activity-Tracker-browser-extension/popup.html
new file mode 100644
index 0000000..2086671
--- /dev/null
+++ b/packages/Activity-Tracker-browser-extension/popup.html
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+ Document
+
+
+
+ Activity
+
+ URL: Loading ...
+
+
+
diff --git a/packages/Activity-Tracker-browser-extension/src/background.js b/packages/Activity-Tracker-browser-extension/src/background.js
new file mode 100644
index 0000000..3ddd104
--- /dev/null
+++ b/packages/Activity-Tracker-browser-extension/src/background.js
@@ -0,0 +1,138 @@
+let title = null;
+let url = null;
+let favicon = null;
+class ActiveBrowserWatcher {
+ /**
+ * @param {number} interval Polling interval
+ * @param {(activity) => void} changeCallback
+ */
+ constructor(interval = 1000, changeCallback) {
+ this.startTime = null;
+ this.title = null; //Title
+ this.url = null;
+ this.favicon = null;
+ this.changeCallback = changeCallback;
+ this.interval = interval;
+ }
+
+ /**
+ * Storing the start time of the active window
+ * Collecting data of the window which will be active
+ */
+ storeTime() {
+ const endTime = Date.now();
+ const startTime = this.startTime;
+
+ const title = this.title;
+ const url = this.url;
+ const favicon = this.favicon;
+
+ const data = {
+ title,
+ url,
+ favicon,
+ startTime,
+ endTime,
+ };
+
+ fetch('http://localhost:32768/api/browseractivities', {
+ method: 'POST',
+ body: data,
+ })
+ .then(console.log('done store'))
+ .catch(err => {
+ console.log(err);
+ });
+ this.changeCallback(data);
+ console.log(data);
+ }
+
+ /**
+ * Checks the active window is specific time interval
+ * and whenever the active window changes stores the time difference by calling {@link ActiveWindowWatcher.storeTime} function
+ */
+ tracker() {
+ setInterval(() => {
+ let queryOptions = { active: true, currentWindow: true }; // to get current active tab from the current window
+ // eslint-disable-next-line no-undef
+ chrome.tabs.query(queryOptions, function currentTab(tabs) {
+ let currentTab = tabs[0]; // take the object from the returned promise
+ let currentTitle = currentTab.title; // take object title
+ let currentUrl = currentTab.url; // take object URL
+ let currentFavIcons = currentTab.favIconUrl;
+ title = currentTitle;
+ url = currentUrl;
+ favicon = currentFavIcons;
+
+ // Title
+ const activityTitle = document.getElementById('activityTitle');
+ const activityTitleUrl = document.getElementById('activityTitle');
+ activityTitle.innerHTML = 'Title: ' + currentTitle; //format it in html
+ activityTitleUrl.setAttribute('href', currentUrl);
+ console.log(activityTitle);
+
+ // URl
+ const activityUrl = document.getElementById('activityUrl');
+ const activityLink = document.getElementById('activityUrl');
+ activityUrl.innerHTML = 'URL: ' + currentUrl; //format it in html
+ activityLink.setAttribute('href', currentUrl);
+
+ // Favicon
+ const activityFavicon = document.getElementById('activityFavicon');
+ activityFavicon.setAttribute('src', currentFavIcons); //format Favicon in html
+ });
+
+ if (title === undefined) {
+ this.title = null;
+ this.url = null;
+ this.favicon = null;
+ return;
+ }
+
+ if (!this.title) {
+ this.startTime = Date.now();
+ this.title = title;
+ this.url = url;
+ this.favicon = favicon;
+ }
+
+ //If the active window is changed store the used time data.
+ if (title !== this.title) {
+ this.storeTime();
+ this.title = null;
+ this.url = null;
+ this.favicon = null;
+ }
+ console.log(title, url, favicon, this.startTime);
+ }, this.interval);
+ }
+
+ initialize() {
+ this.tracker();
+ }
+}
+
+// const activityTracker = new ActiveBrowserWatcher(1000);
+
+// const activityTracker = new ActiveBrowserWatcher(1000, activity => {
+// saveActivities(activity);
+// });
+
+// fetch('http://localhost:32768/api/browseractivities',{
+// method: 'POST',
+// body: activityTracker
+// )
+// });
+
+const activityTracker = new ActiveBrowserWatcher(1000, activity => {
+ fetch('http://localhost:32768/api/browseractivities', {
+ method: 'POST',
+ body: activity,
+ })
+ .then(console.log('done'))
+ .catch(err => {
+ console.log(err);
+ });
+});
+
+activityTracker.initialize();
diff --git a/packages/Activity-Tracker-browser-extension/style/popup.css b/packages/Activity-Tracker-browser-extension/style/popup.css
new file mode 100644
index 0000000..228faca
--- /dev/null
+++ b/packages/Activity-Tracker-browser-extension/style/popup.css
@@ -0,0 +1,20 @@
+body {
+ width: 300px;
+ height: 300px;
+ text-align: center;
+ background-color: rgb(18, 31, 48);
+}
+
+.textBody {
+ color: aliceblue;
+}
+
+#activityFavicon {
+ margin-top: 50px;
+ margin-bottom: 50px;
+}
+
+img {
+ width: 20%;
+ height: auto;
+}
diff --git a/packages/server/app.js b/packages/server/app.js
index 70a0782..feeda99 100644
--- a/packages/server/app.js
+++ b/packages/server/app.js
@@ -9,6 +9,7 @@ import root from './routes/root.routes.js';
import user from './routes/user.routes.js';
import activity from './routes/activity.routes.js';
import app_usage from './routes/app.routes.js';
+import browser_activity from './routes/browseractivity.routes.js';
const app = express();
@@ -41,6 +42,7 @@ app.use('/api/', root);
app.use('/api/users', user);
app.use('/api/activities', activity);
app.use('/api/apps', app_usage);
+app.use('/api/browseractivities', browser_activity);
app.listen(port, hostname, function () {
console.log(`Nodejs server running at http://${hostname}:${port}/`);
diff --git a/packages/server/controllers/index.js b/packages/server/controllers/index.js
index c7f8531..20f7b71 100644
--- a/packages/server/controllers/index.js
+++ b/packages/server/controllers/index.js
@@ -2,10 +2,12 @@ import { useLocal } from '../config.js';
import * as json_activity_controller from './json/activity.controller.js';
import * as json_app_controller from './json/app.controller.js';
+import * as json_browser_activity_controller from './json/browseractivity.controller.js';
import * as mongo_activity_controller from './mongo/activity.controller.js';
import * as mongo_app_controller from './mongo/app.controller.js';
import * as mongo_user_controller from './mongo/user.controller.js';
+import * as mongo_browser_activity_controller from './mongo/browserTracker.controller.js';
export const activity_controller = useLocal
? json_activity_controller
@@ -16,3 +18,7 @@ export const app_controller = useLocal
: mongo_app_controller;
export const user_controller = mongo_user_controller;
+
+export const browser_activity_controller = useLocal
+ ? json_browser_activity_controller
+ : mongo_browser_activity_controller;
diff --git a/packages/server/controllers/json/browseractivity.controller.js b/packages/server/controllers/json/browseractivity.controller.js
new file mode 100644
index 0000000..bd5e974
--- /dev/null
+++ b/packages/server/controllers/json/browseractivity.controller.js
@@ -0,0 +1,87 @@
+import fs from 'fs';
+import path from 'path';
+import envPaths from 'env-paths';
+import { extract } from './utils.js';
+
+const dirname = envPaths('ActivityTracker').data;
+const browserActivityDir = path.join(dirname, '/browser-activity');
+fs.mkdirSync(browserActivityDir, { recursive: true });
+
+/**
+ * Get date string of format YYYY-MM-DD
+ * @param {Date} date
+ */
+const getISODateString = date => date.toISOString().slice(0, 10);
+
+/**
+ * @param {Date} date
+ */
+function getFilePath(date) {
+ const filename = `${getISODateString(date)}.json`;
+ return path.join(browserActivityDir, filename);
+}
+
+/**
+ * Get Activities for a given date
+ * @param {Date} date
+ * @returns {Object[]}
+ */
+function getActivities(date) {
+ let data = [];
+ try {
+ data = JSON.parse(fs.readFileSync(getFilePath(date), 'utf-8'));
+ } catch (error) {
+ if (error.code === 'ENOENT') {
+ data = [];
+ } else {
+ throw error;
+ }
+ }
+ return data;
+}
+
+const getDataFromJson = (start, end) => {
+ start = new Date(getISODateString(new Date(start)));
+ end = new Date(getISODateString(new Date(end)));
+
+ let result = [];
+ let date = start;
+
+ while (date <= end) {
+ try {
+ result.push(...JSON.parse(fs.readFileSync(getFilePath(date), 'utf-8')));
+ } catch (error) {
+ console.log(`No data for ${getISODateString(date)}`);
+ }
+ date.setDate(date.getDate() + 1);
+ }
+ return result;
+};
+
+export const activity_create = (req, res) => {
+ const packet = extract(req.body, [
+ 'title',
+ 'url',
+ 'favicon',
+ 'startTime',
+ 'endTime',
+ ]);
+ const filepath = getFilePath(new Date(packet.endTime));
+ const data = getActivities(new Date(packet.endTime));
+ data.push(packet);
+ fs.writeFileSync(filepath, JSON.stringify(data, null, 4));
+
+ res.send('Activity created successfully');
+};
+
+export const all_activities = (req, res) => {
+ const today = new Date();
+ const yesterday = new Date(today);
+ yesterday.setDate(yesterday.getDate() - 1);
+
+ const after = req.query.after ?? yesterday.toISOString();
+ const before = req.query.before ?? today.toISOString();
+
+ const activities = getDataFromJson(after, before);
+ res.json(activities);
+};
diff --git a/packages/server/controllers/json/utils.js b/packages/server/controllers/json/utils.js
index 2c9dab5..d716156 100644
--- a/packages/server/controllers/json/utils.js
+++ b/packages/server/controllers/json/utils.js
@@ -39,3 +39,6 @@ export const groupBy = (array, key) => {
return result;
}, {}); // empty object is the initial value for result object
};
+
+export const extract = (obj, keys) =>
+ Object.fromEntries(keys.map(k => [k, obj[k]]));
diff --git a/packages/server/controllers/mongo/browserTracker.controller.js b/packages/server/controllers/mongo/browserTracker.controller.js
new file mode 100644
index 0000000..0d5f38c
--- /dev/null
+++ b/packages/server/controllers/mongo/browserTracker.controller.js
@@ -0,0 +1,36 @@
+import browserActivity from '../../models/browserTracker.model.js';
+
+export const activity_create = (req, res, next) => {
+ const { title, url, favicon, startTime, endTime } = req.body;
+ const browseractivity = new browserActivity({
+ title,
+ url,
+ favicon,
+ startTime,
+ endTime,
+ });
+ browseractivity.save(err => {
+ if (err) return next(err);
+ });
+
+ res.send('Activity created succecssfully');
+};
+
+export const all_activities = (req, res, next) => {
+ const today = new Date();
+ const yesterday = new Date(today);
+ yesterday.setDate(yesterday.getDate() - 1);
+
+ const after = req.query.after ?? yesterday.toISOString();
+ const before = req.query.before ?? today.toISOString();
+
+ browserActivity.find(
+ {
+ startTime: { $gte: after, $lt: before },
+ },
+ (err, activity) => {
+ if (err) return next(err);
+ res.json(activity);
+ },
+ );
+};
diff --git a/packages/server/models/browserTracker.model.js b/packages/server/models/browserTracker.model.js
new file mode 100644
index 0000000..00017af
--- /dev/null
+++ b/packages/server/models/browserTracker.model.js
@@ -0,0 +1,13 @@
+import mongoose from 'mongoose';
+const Schema = mongoose.Schema;
+
+// Schema
+const browserActivitySchema = new Schema({
+ title: { type: String, required: true },
+ url: { type: String, required: true },
+ favicon: { type: String, required: true },
+ startTime: { type: Date, required: true },
+ endTime: { type: Date, required: true },
+});
+
+export default mongoose.model('browserActivity', browserActivitySchema);
diff --git a/packages/server/routes/browseractivity.routes.js b/packages/server/routes/browseractivity.routes.js
new file mode 100644
index 0000000..d7e9867
--- /dev/null
+++ b/packages/server/routes/browseractivity.routes.js
@@ -0,0 +1,10 @@
+import express from 'express';
+import auth from '../middlewares/auth.js';
+import { browser_activity_controller } from '../controllers/index.js';
+
+const router = express.Router();
+
+router.get('/', auth, browser_activity_controller.all_activities);
+router.post('/', auth, browser_activity_controller.activity_create);
+
+export default router;
diff --git a/packages/server/routes/root.routes.js b/packages/server/routes/root.routes.js
index 3e1b73f..2bb2082 100644
--- a/packages/server/routes/root.routes.js
+++ b/packages/server/routes/root.routes.js
@@ -8,6 +8,7 @@ router.get('/', (req, res) => {
const routes = {
apps: `http://localhost:${port}/api/apps`,
activities: `http://localhost:${port}/api/activities`,
+ browseractivities: `http://localhost:${port}/api/browseractivities`,
};
if (!useLocal) routes.users = `http://localhost:${port}/api/users`;
res.json(routes);