Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
3de9f1b
feat: integrate google analytics to track page views across all react…
elainefan331 Oct 24, 2025
17a8b77
Add backend setup packages
elainefan331 Oct 24, 2025
c386f7f
add .sequlizerc and config files
elainefan331 Oct 24, 2025
61fe73b
add backend source directories and models
elainefan331 Oct 27, 2025
2afa8af
defined associations between tables
elainefan331 Oct 27, 2025
e914448
add server.js to start the backend server
elainefan331 Oct 27, 2025
cb77cba
updated package.json script
elainefan331 Oct 28, 2025
dd1b3e3
add migration files for database set up
elainefan331 Oct 29, 2025
26ade55
update users migration file; closes #104
elainefan331 Oct 29, 2025
d85c503
add register function
elainefan331 Oct 29, 2025
21be511
add login function
elainefan331 Oct 30, 2025
54e5fbd
implement HttpOnly cookie-based authentication
elainefan331 Oct 31, 2025
a35b749
add create dataset and like a dataset functions
elainefan331 Oct 31, 2025
e5c4b8d
add unlike a dataset and save a dataset functions
elainefan331 Oct 31, 2025
436b7ae
add unsave a dataset function
elainefan331 Oct 31, 2025
0737057
add addcomment to a dataset function
elainefan331 Nov 3, 2025
cda5ceb
add getComments function
elainefan331 Nov 3, 2025
947cbb9
add deleteComment function
elainefan331 Nov 3, 2025
ae28774
add updateComment function
elainefan331 Nov 3, 2025
7c5b6e0
add getUserSavedDatasets and trackView functions
elainefan331 Nov 3, 2025
4ed1fcd
add datasets route and endpoints
elainefan331 Nov 4, 2025
482cca6
modify trackView function to check if the dataset has been viewed bef…
elainefan331 Nov 4, 2025
a6da1b3
add recently viewed endpoint
elainefan331 Nov 4, 2025
49cfb7e
add get most viewed datasets endpoint
elainefan331 Nov 4, 2025
b98a2f3
add user button on the nav bar
elainefan331 Nov 5, 2025
83bbe3d
add users routes
elainefan331 Nov 10, 2025
6e5aa8c
add dbs routes
elainefan331 Nov 10, 2025
99cd3be
add datasets routes
elainefan331 Nov 10, 2025
c25a1d2
add activities routes
elainefan331 Nov 10, 2025
c83cb0f
add getDbStats and getDbInfo functions
elainefan331 Nov 10, 2025
27d7e09
add cross database search function
elainefan331 Nov 10, 2025
ffadbca
add get dataset detail function
elainefan331 Nov 10, 2025
743ce10
modify like route
elainefan331 Nov 10, 2025
75e670d
modify unlike dataset function
elainefan331 Nov 12, 2025
3b500ee
modify save dataset function
elainefan331 Nov 12, 2025
6aa6403
modify unsave dataset function
elainefan331 Nov 12, 2025
b68b16f
modify add comment function
elainefan331 Nov 12, 2025
78e8f4a
modify get all comments for a dataset function
elainefan331 Nov 12, 2025
907581d
modify update a comment function
elainefan331 Nov 12, 2025
5324a42
modify delete a comment function
elainefan331 Nov 12, 2025
804a65c
modify track view function
elainefan331 Nov 12, 2025
d98748f
add users route and controller file
elainefan331 Nov 12, 2025
1a77216
modify track views controller
elainefan331 Nov 12, 2025
4458bbf
modify the timestamp of veiwed history
elainefan331 Nov 13, 2025
5249a98
clean the console log in activity controller
elainefan331 Nov 13, 2025
04aa905
adjust age display in subjectcard
elainefan331 Nov 17, 2025
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
8 changes: 7 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,10 @@ node_modules
.DS_Store
package-lock.json

build
build

#backend
backend/node_modules/
backend/.env
backend/*.sqlite
!backend/package-lock.json
23 changes: 23 additions & 0 deletions backend/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Server Configuration
PORT=5000
NODE_ENV=development

# Database Configuration
# For local development (SQLite)
DB_DIALECT=sqlite
DB_STORAGE=./database.sqlite

# For production (PostgreSQL) - uncomment and fill these
# DB_DIALECT=postgres
# DB_HOST=localhost
# DB_PORT=5432
# DB_NAME=neurojson_db
# DB_USER=your_username
# DB_PASSWORD=your_password

# JWT Secret
JWT_SECRET=your-super-secret-jwt-key-change-this-in-production

# CORS Origin (your React frontend URL)
CORS_ORIGIN=http://localhost:3000

28 changes: 28 additions & 0 deletions backend/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Environment variables
.env
.env.local
.env.*.local
!.env.example

# Database files (SQLite)
*.sqlite
*.sqlite3
*.db

# Dependencies
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Logs
logs/
*.log

# OS files
.DS_Store
Thumbs.db

# Build output
dist/
build/
9 changes: 9 additions & 0 deletions backend/.sequelizerc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// backend/.sequelizerc
const path = require("path");

module.exports = {
config: path.resolve("config", "config.js"),
"models-path": path.resolve("src", "models"),
"seeders-path": path.resolve("seeders"),
"migrations-path": path.resolve("migrations"),
};
29 changes: 29 additions & 0 deletions backend/config/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
require("dotenv").config();

module.exports = {
development: {
dialect: "sqlite",
storage: "./database.sqlite",
logging: console.log,
},
test: {
dialect: "sqlite",
storage: ":memory:",
logging: false,
},
production: {
dialect: "postgres",
host: process.env.DB_HOST,
port: parseInt(process.env.DB_PORT || "5432"),
database: process.env.DB_NAME,
username: process.env.DB_USER,
password: process.env.DB_PASSWORD,
logging: false,
pool: {
max: 5,
min: 0,
acquire: 30000,
idle: 10000,
},
},
};
70 changes: 70 additions & 0 deletions backend/migrations/20251028184256-create-users.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
"use strict";

/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
/**
* Add altering commands here.
*
* Example:
* await queryInterface.createTable('users', { id: Sequelize.INTEGER });
*/
await queryInterface.createTable("users", {
id: {
type: Sequelize.INTEGER,
autoIncrement: true,
primaryKey: true,
allowNull: false,
},
orcid_id: {
type: Sequelize.STRING(255),
allowNull: true,
unique: true,
},
google_id: {
type: Sequelize.STRING(255),
allowNull: true,
unique: true,
},
github_id: {
type: Sequelize.STRING(255),
allowNull: true,
unique: true,
},
username: {
type: Sequelize.STRING(255),
allowNull: false,
unique: true,
},
hashed_password: {
type: Sequelize.STRING(255),
allowNull: true,
},
email: {
type: Sequelize.STRING(255),
allowNull: false,
unique: true,
},
created_at: {
type: Sequelize.DATE,
allowNull: false,
defaultValue: Sequelize.literal("CURRENT_TIMESTAMP"),
},
updated_at: {
type: Sequelize.DATE,
allowNull: false,
defaultValue: Sequelize.literal("CURRENT_TIMESTAMP"),
},
});
},

async down(queryInterface, Sequelize) {
/**
* Add reverting commands here.
*
* Example:
* await queryInterface.dropTable('users');
*/
await queryInterface.dropTable("users");
},
};
37 changes: 37 additions & 0 deletions backend/migrations/20251028184358-create-datasets.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"use strict";

/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.createTable("datasets", {
id: {
type: Sequelize.INTEGER,
autoIncrement: true,
primaryKey: true,
allowNull: false,
},
couch_db: {
type: Sequelize.STRING(255),
allowNull: false,
},
ds_id: {
type: Sequelize.STRING(255),
allowNull: false,
},
views_count: {
type: Sequelize.INTEGER,
allowNull: true,
defaultValue: 0,
},
});
// Add unique constraint on couch_db + ds_id
await queryInterface.addIndex("datasets", ["couch_db", "ds_id"], {
unique: true,
name: "datasets_couch_db_ds_id_unique",
});
},

async down(queryInterface, Sequelize) {
await queryInterface.dropTable("datasets");
},
};
50 changes: 50 additions & 0 deletions backend/migrations/20251028184628-create-dataset-likes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
"use strict";

/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.createTable("dataset_likes", {
id: {
type: Sequelize.INTEGER,
autoIncrement: true,
primaryKey: true,
allowNull: false,
},
user_id: {
type: Sequelize.INTEGER,
allowNull: false,
references: {
model: "users",
key: "id",
},
onUpdate: "CASCADE",
onDelete: "CASCADE",
},
dataset_id: {
type: Sequelize.INTEGER,
allowNull: false,
references: {
model: "datasets",
key: "id",
},
onUpdate: "CASCADE",
onDelete: "CASCADE",
},
created_at: {
type: Sequelize.DATE,
allowNull: false,
defaultValue: Sequelize.literal("CURRENT_TIMESTAMP"),
},
});

// Add unique constraint to prevent duplicate likes
await queryInterface.addIndex("dataset_likes", ["user_id", "dataset_id"], {
unique: true,
name: "dataset_likes_user_dataset_unique",
});
},

async down(queryInterface, Sequelize) {
await queryInterface.dropTable("dataset_likes");
},
};
50 changes: 50 additions & 0 deletions backend/migrations/20251028184649-create-saved-datasets.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
"use strict";

/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.createTable("saved_datasets", {
id: {
type: Sequelize.INTEGER,
autoIncrement: true,
primaryKey: true,
allowNull: false,
},
user_id: {
type: Sequelize.INTEGER,
allowNull: false,
references: {
model: "users",
key: "id",
},
onUpdate: "CASCADE",
onDelete: "CASCADE",
},
dataset_id: {
type: Sequelize.INTEGER,
allowNull: false,
references: {
model: "datasets",
key: "id",
},
onUpdate: "CASCADE",
onDelete: "CASCADE",
},
created_at: {
type: Sequelize.DATE,
allowNull: false,
defaultValue: Sequelize.literal("CURRENT_TIMESTAMP"),
},
});

// Add unique constraint to prevent duplicate saves
await queryInterface.addIndex("saved_datasets", ["user_id", "dataset_id"], {
unique: true,
name: "saved_datasets_user_dataset_unique",
});
},

async down(queryInterface, Sequelize) {
await queryInterface.dropTable("saved_datasets");
},
};
57 changes: 57 additions & 0 deletions backend/migrations/20251028184704-create-comments.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
"use strict";

/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.createTable("comments", {
id: {
type: Sequelize.INTEGER,
autoIncrement: true,
primaryKey: true,
allowNull: false,
},
user_id: {
type: Sequelize.INTEGER,
allowNull: false,
references: {
model: "users",
key: "id",
},
onUpdate: "CASCADE",
onDelete: "CASCADE",
},
dataset_id: {
type: Sequelize.INTEGER,
allowNull: false,
references: {
model: "datasets",
key: "id",
},
onUpdate: "CASCADE",
onDelete: "CASCADE",
},
body: {
type: Sequelize.TEXT,
allowNull: false,
},
created_at: {
type: Sequelize.DATE,
allowNull: false,
defaultValue: Sequelize.literal("CURRENT_TIMESTAMP"),
},
updated_at: {
type: Sequelize.DATE,
allowNull: false,
defaultValue: Sequelize.literal("CURRENT_TIMESTAMP"),
},
});

// Add indexes for common queries
await queryInterface.addIndex("comments", ["dataset_id"]);
await queryInterface.addIndex("comments", ["user_id"]);
},

async down(queryInterface, Sequelize) {
await queryInterface.dropTable("comments");
},
};
Loading