Skip to content

Commit edf034d

Browse files
committed
feat(opt): rm user-in-project:projectId collections usage
1 parent 366a7e7 commit edf034d

File tree

5 files changed

+140
-98
lines changed

5 files changed

+140
-98
lines changed
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
require('dotenv').config();
2+
require('process');
3+
const { MongoClient } = require('mongodb');
4+
5+
/**
6+
* Method that runs convertor script
7+
*/
8+
async function run() {
9+
const fullUri = process.env.MONGO_HAWK_DB_URL;
10+
11+
// Parse the Mongo URL manually
12+
const mongoUrl = new URL(fullUri);
13+
const hawkDatabaseName = 'hawk';
14+
15+
// Extract query parameters
16+
const queryParams = Object.fromEntries(mongoUrl.searchParams.entries());
17+
18+
// Compose connection options manually
19+
const options = {
20+
useNewUrlParser: true,
21+
useUnifiedTopology: true,
22+
authSource: queryParams.authSource || 'admin',
23+
replicaSet: queryParams.replicaSet || undefined,
24+
tls: queryParams.tls === 'true',
25+
tlsInsecure: queryParams.tlsInsecure === 'true',
26+
// connectTimeoutMS: 3600000,
27+
// socketTimeoutMS: 3600000,
28+
};
29+
30+
// Remove query string from URI
31+
mongoUrl.search = '';
32+
const cleanUri = mongoUrl.toString();
33+
34+
console.log('Connecting to:', cleanUri);
35+
console.log('With options:', options);
36+
37+
const client = new MongoClient(cleanUri, options);
38+
39+
await client.connect();
40+
const hawkDb = client.db(hawkDatabaseName);
41+
42+
console.log(`Connected to database: ${hawkDatabaseName}`);
43+
44+
const collections = await hawkDb.listCollections({}, {
45+
authorizedCollections: true,
46+
nameOnly: true,
47+
}).toArray();
48+
49+
let usersInProjectCollectionsToCheck = collections.filter(col => /^users-in-project:/.test(col.name)).map(col => col.name);
50+
51+
console.log(`Found ${usersInProjectCollectionsToCheck.length} users in project collections.`);
52+
53+
const usersDocuments = await hawkDb.collection('users').find({}).toArray();
54+
55+
// Convert events
56+
let i = 1;
57+
let documentsUpdatedCount = 1;
58+
59+
for (const collectionName of usersInProjectCollectionsToCheck) {
60+
console.log(`[${i}/${usersInProjectCollectionsToCheck.length}] Processing ${collectionName}`);
61+
62+
const usersInProject = await hawkDb.collection(collectionName).find({}).toArray();
63+
64+
console.log(`Found ${usersInProject.length} users in project ${collectionName}.`);
65+
66+
let usersUpdatedCount = 0;
67+
68+
for (const userInProject of usersInProject) {
69+
const userDocument = usersDocuments.find(u => u._id.toString() === userInProject.userId.toString());
70+
if (userDocument) {
71+
const projectId = collectionName.split(':')[1];
72+
await hawkDb.collection('users').updateOne({ _id: userDocument._id }, { $set: { projectsLastVisit: { [projectId]: userInProject.timestamp } } });
73+
usersUpdatedCount++;
74+
console.log(`Updated ${usersUpdatedCount}/${usersInProject.length} users in project ${collectionName}.`);
75+
}
76+
}
77+
78+
i++;
79+
}
80+
81+
await client.close();
82+
}
83+
84+
run().catch(err => {
85+
console.error('❌ Script failed:', err);
86+
process.exit(1);
87+
});

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"dev:up": "docker-compose -f docker-compose.dev.yml up -d",
1212
"dev:down": "docker-compose -f docker-compose.dev.yml down",
1313
"build": "tsc",
14+
"convert": "node ./convertors/set-user-project-last-visit.js",
1415
"migrations:create": "docker-compose exec api yarn migrate-mongo create",
1516
"migrations:up": "docker-compose exec api yarn migrate-mongo up",
1617
"migrations:down": "docker-compose exec api yarn migrate-mongo down",

src/models/user.ts

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ export interface UserNotificationsDBScheme {
6262
/**
6363
* Types of notifications to receive
6464
*/
65-
whatToReceive: {[key in UserNotificationType]: boolean};
65+
whatToReceive: { [key in UserNotificationType]: boolean };
6666
}
6767

6868
/**
@@ -85,6 +85,11 @@ export enum UserNotificationType {
8585
SystemMessages = 'SystemMessages',
8686
}
8787

88+
/**
89+
* This structure represents how user projects last visit is stored at the DB (in 'users' collection)
90+
*/
91+
type UserProjectsLastVisitDBScheme = Record<string, number>;
92+
8893
/**
8994
* User model
9095
*/
@@ -130,6 +135,11 @@ export default class UserModel extends AbstractModel<UserDBScheme> implements Us
130135
*/
131136
public notifications!: UserNotificationsDBScheme;
132137

138+
/**
139+
* User projects last visit
140+
*/
141+
public projectsLastVisit!: UserProjectsLastVisitDBScheme;
142+
133143
/**
134144
* Saved bank cards for one-click payments
135145
*/
@@ -233,6 +243,32 @@ export default class UserModel extends AbstractModel<UserDBScheme> implements Us
233243
}
234244
}
235245

246+
/**
247+
* Update user's last project visit
248+
*
249+
* @param projectId - project id
250+
*/
251+
public async updateLastProjectVisit(projectId: string): Promise<number> {
252+
const time = Date.now() / 1000;
253+
254+
await this.update(
255+
{ _id: new ObjectId(this._id) },
256+
{ [`projectsLastVisit.${projectId}`]: time }
257+
);
258+
259+
return time;
260+
}
261+
262+
/**
263+
* Get user's last project visit
264+
*
265+
* @param projectId - project id
266+
* @returns {Promise<number>}
267+
*/
268+
public async getLastProjectVisit(projectId: string): Promise<number> {
269+
return this.projectsLastVisit?.[projectId] || 0;
270+
}
271+
236272
/**
237273
* Update user profile data
238274
* @param user – user object
@@ -323,7 +359,7 @@ export default class UserModel extends AbstractModel<UserDBScheme> implements Us
323359
* Remove workspace from membership collection
324360
* @param workspaceId - id of workspace to remove
325361
*/
326-
public async removeWorkspace(workspaceId: string): Promise<{workspaceId: string}> {
362+
public async removeWorkspace(workspaceId: string): Promise<{ workspaceId: string }> {
327363
await this.membershipCollection.deleteOne({
328364
workspaceId: new ObjectId(workspaceId),
329365
});

src/models/userInProject.js

Lines changed: 0 additions & 89 deletions
This file was deleted.

src/resolvers/project.js

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import * as telegram from '../utils/telegram';
33
const mongo = require('../mongo');
44
const { ApolloError, UserInputError } = require('apollo-server-express');
55
const Validator = require('../utils/validator');
6-
const UserInProject = require('../models/userInProject');
76
const EventsFactory = require('../models/eventsFactory');
87
const getEventsFactory = require('./helpers/eventsFactory').default;
98
const ProjectToWorkspace = require('../models/projectToWorkspace');
@@ -358,10 +357,14 @@ module.exports = {
358357
* @param {Context.user} user - current authorized user {@see ../index.js}
359358
* @return {Promise<Number>}
360359
*/
361-
async updateLastProjectVisit(_obj, { projectId }, { user }) {
362-
const userInProject = new UserInProject(user.id, projectId);
360+
async updateLastProjectVisit(_obj, { projectId }, { user, factories }) {
361+
const userModel = await factories.usersFactory.findById(user.id);
363362

364-
return userInProject.updateLastVisit();
363+
if (!userModel) {
364+
throw new ApolloError('User not found');
365+
}
366+
367+
return userModel.updateLastProjectVisit(projectId);
365368
},
366369
},
367370
Project: {
@@ -422,10 +425,14 @@ module.exports = {
422425
*
423426
* @return {Promise<number>}
424427
*/
425-
async unreadCount(project, data, { user, ...context }) {
428+
async unreadCount(project, _args, { factories, user, ...context }) {
426429
const eventsFactory = getEventsFactory(context, project._id);
427-
const userInProject = new UserInProject(user.id, project._id);
428-
const lastVisit = await userInProject.getLastVisit();
430+
const userModel = await factories.usersFactory.findById(user.id);
431+
432+
if (!userModel) {
433+
throw new ApolloError('User not found');
434+
}
435+
const lastVisit = await userModel.getLastProjectVisit(project._id);
429436

430437
return eventsFactory.getUnreadCount(lastVisit);
431438
},

0 commit comments

Comments
 (0)