Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ node_modules
/.vscode/launch.json
.DS_Store
dump.rdb
backend/secrets
1,266 changes: 709 additions & 557 deletions backend/package-lock.json

Large diffs are not rendered by default.

9 changes: 7 additions & 2 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,19 @@
"dotenv": "^16.0.0",
"express": "^4.17.3",
"express-jwt": "^7.7.0",
"gridfs-stream": "^1.1.1",
"ioredis": "^5.3.2",
"jsonwebtoken": "^8.5.1",
"migrate-mongo": "^9.0.0",
"mongo-dot-notation": "^3.1.0",
"mongoose": "^6.3.2",
"mongoose": "^6.7.5",
"mongoose-autopopulate": "^0.17.1",
"multer": "^1.4.5-lts.1",
"nodemon": "^2.0.16",
"prettier": "^2.8.8",
"socket.io": "^4.7.5",
"uuid": "^8.3.2"
"uuid": "^8.3.2",
"mongodb": "4.11.0"
},
"scripts": {
"build": "tsc",
Expand All @@ -41,7 +44,9 @@
"devDependencies": {
"@types/crypto-js": "^4.1.1",
"@types/express": "^4.17.13",
"@types/gridfs-stream": "^0.5.39",
"@types/jsonwebtoken": "^8.5.8",
"@types/multer": "^1.4.12",
"@types/node": "^18.19.50",
"@types/uuid": "^8.3.4",
"@typescript-eslint/eslint-plugin": "^5.22.0",
Expand Down
79 changes: 78 additions & 1 deletion backend/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ import todoItems from './api/todoItem';
import learner from './api/learner';
import { isAuthenticated } from './utils/auth';
import RedisClient from './utils/redis';

import Grid from 'gridfs-stream';
import multer from 'multer';
import { GridFSBucket } from 'mongodb';

import aiRouter from './api/ai';
import chatHistoryRouter from './api/chatHistory';
dotenv.config();
Expand All @@ -33,7 +38,7 @@ const dbName = process.env.DB_NAME;
const dbURI = `mongodb+srv://${dbUsername}:${dbPassword}@${dbUrl}.mongodb.net/${dbName}?retryWrites=true&w=majority`;

const redisHost = process.env.REDIS_HOST || 'localhost';
const redisPort = (process.env.REDIS_PORT || 6379) as number;
const redisPort = Number(process.env.REDIS_PORT) || 6379;
const redisPassword = process.env.REDIS_PASSWORD || '';

const app = express();
Expand Down Expand Up @@ -91,3 +96,75 @@ mongoose
console.log('HTTP server running at ' + port);
})
.catch((err) => console.log(err));

const connection = mongoose.connection;
let gfs: Grid.Grid;
let gridFSBucket: GridFSBucket; // Use GridFSBucket type directly

connection.once('open', () => {
gridFSBucket = new GridFSBucket(connection.db, {
bucketName: 'uploads',
});
gfs = Grid(connection.db, mongoose.mongo);
gfs.collection('uploads'); // Set the GridFS collection
console.log('MongoDB connected and GridFS set up');
});

const storage = multer.memoryStorage(); // Store files in memory temporarily
const upload = multer({ storage });

// Upload image to MongoDB using GridFS
app.post('/api/upload', upload.single('file'), (req, res) => {
if (!req.file) {
return res.status(400).send('No file uploaded.');
}

const uploadStream = gridFSBucket.openUploadStream(req.file.originalname, {
contentType: req.file.mimetype,
});

uploadStream.on('error', (error: Error) => {
return res.status(500).send('Error uploading file: ' + error.message);
});

uploadStream.on('finish', () => {
const fileId = uploadStream.id;

// Return the uploaded file ID to the client
res.status(201).json({
message: 'File uploaded successfully',
fileId: fileId,
// Optionally return other details if needed
});
});

uploadStream.end(req.file.buffer); // No callback here
});

// Delete image from MongoDB using GridFS
app.delete('/api/upload/:id', (req, res) => {
const fileId = new mongoose.Types.ObjectId(req.params.id);

gridFSBucket.delete(fileId, (err) => {
if (err) {
return res
.status(500)
.json({ message: 'Error deleting file', error: err.message });
}
return res.status(200).json({ message: 'File deleted successfully' });
});
});

app.get('/api/image/:id', (req, res) => {
const fileId = new mongoose.Types.ObjectId(req.params.id);

gfs.files.findOne({ _id: fileId }, (err, file) => {
if (!file || file.length === 0) {
return res.status(404).json({ message: 'File not found' });
}

// File found, stream it to the response
const readStream = gridFSBucket.openDownloadStream(file._id);
readStream.pipe(res);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,8 @@ <h1 mat-dialog-title>Board Configuration</h1>
<mat-tab-group dynamicHeight animationDuration="0ms">
<mat-tab label="Board">
<h2 style="margin-top: 20px">Board Type</h2>
<mat-radio-group
[(ngModel)]="boardType"
>
<mat-radio-button
style="margin-left: 10px"
value="BRAINSTORMING"
>
<mat-radio-group [(ngModel)]="boardType">
<mat-radio-button style="margin-left: 10px" value="BRAINSTORMING">
Idea generation
</mat-radio-button>
<mat-radio-button style="margin-left: 10px" value="QUESTION_AUTHORING">
Expand Down Expand Up @@ -77,33 +72,79 @@ <h2 style="margin-top: 20px">Board Type</h2>
<p>
<label><strong>Default View</strong></label>
<mat-radio-group [(ngModel)]="defaultView">
<mat-radio-button style="margin-left: 10px;" value="CANVAS"
[disabled]="viewSettings.allowCanvas === false">Canvas</mat-radio-button>
<mat-radio-button style="margin-left: 10px;" value="WORKSPACE"
[disabled]="viewSettings.allowWorkspace === false">Workspace</mat-radio-button>
<mat-radio-button style="margin-left: 10px;" value="BUCKETS"
[disabled]="viewSettings.allowBuckets === false">Buckets</mat-radio-button>
<mat-radio-button style="margin-left: 10px;" value="MONITOR"
[disabled]="viewSettings.allowMonitor === false">Monitor</mat-radio-button>
<mat-radio-button
style="margin-left: 10px"
value="CANVAS"
[disabled]="viewSettings.allowCanvas === false"
>Canvas</mat-radio-button
>
<mat-radio-button
style="margin-left: 10px"
value="WORKSPACE"
[disabled]="viewSettings.allowWorkspace === false"
>Workspace</mat-radio-button
>
<mat-radio-button
style="margin-left: 10px"
value="BUCKETS"
[disabled]="viewSettings.allowBuckets === false"
>Buckets</mat-radio-button
>
<mat-radio-button
style="margin-left: 10px"
value="MONITOR"
[disabled]="viewSettings.allowMonitor === false"
>Monitor</mat-radio-button
>
</mat-radio-group>
</p>
<p>
<label><strong>Enable/Disable Views</strong></label>
<mat-checkbox style="margin-left: 10px;" [(ngModel)]="viewSettings.allowCanvas"
[disabled]="defaultView === 'CANVAS'">Canvas</mat-checkbox>
<mat-checkbox style="margin-left: 10px;" [(ngModel)]="viewSettings.allowWorkspace"
[disabled]="defaultView === 'WORKSPACE'">Workspace</mat-checkbox>
<mat-checkbox style="margin-left: 10px;" [(ngModel)]="viewSettings.allowBuckets"
[disabled]="defaultView === 'BUCKETS'">Buckets</mat-checkbox>
<mat-checkbox style="margin-left: 10px;" [(ngModel)]="viewSettings.allowMonitor"
[disabled]="defaultView === 'MONITOR'">Monitor</mat-checkbox>
<mat-checkbox
style="margin-left: 10px"
[(ngModel)]="viewSettings.allowCanvas"
[disabled]="defaultView === 'CANVAS'"
>Canvas</mat-checkbox
>
<mat-checkbox
style="margin-left: 10px"
[(ngModel)]="viewSettings.allowWorkspace"
[disabled]="defaultView === 'WORKSPACE'"
>Workspace</mat-checkbox
>
<mat-checkbox
style="margin-left: 10px"
[(ngModel)]="viewSettings.allowBuckets"
[disabled]="defaultView === 'BUCKETS'"
>Buckets</mat-checkbox
>
<mat-checkbox
style="margin-left: 10px"
[(ngModel)]="viewSettings.allowMonitor"
[disabled]="defaultView === 'MONITOR'"
>Monitor</mat-checkbox
>
</p>
<h4>Set Background Image:</h4>
<mat-chip (click)="compressFile()" color="primary" selected>
<mat-chip
*ngIf="bgImgURL === null"
(click)="uploadImage()"
color="primary"
selected
>
<mat-icon class="chip-icon">upload</mat-icon>
Upload New Background Image
</mat-chip>
<mat-chip *ngIf="bgImgURL"> Image Uploaded </mat-chip>
</mat-tab>
<!-- Hidden file input -->
<input
type="file"
#fileInput
(change)="onFileSelected($event)"
style="display: none"
accept="image/*"
/>
<mat-tab label="Task">
<mat-form-field
appearance="outline"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@ import {
} from 'src/app/models/board';
import { Project } from 'src/app/models/project';
import { Tag } from 'src/app/models/tag';
// import { FileUploadService } from 'src/app/services/fileUpload.service';
import { FileUploadService } from 'src/app/services/fileUpload.service';
import { UserService } from 'src/app/services/user.service';
import { FabricUtils, ImageSettings } from 'src/app/utils/FabricUtils';
import { generateUniqueID } from 'src/app/utils/Utils';
import { TAG_DEFAULT_COLOR } from 'src/app/utils/constants';
import { environment } from 'src/environments/environment';

@Component({
selector: 'app-add-board-modal',
Expand All @@ -27,7 +28,7 @@ import { TAG_DEFAULT_COLOR } from 'src/app/utils/constants';
export class AddBoardModalComponent implements OnInit {
readonly tagDefaultColor = TAG_DEFAULT_COLOR;

boardID: string;
boardID: string = '';

permissions: BoardPermissions;
boardType: BoardType = BoardType.BRAINSTORMING;
Expand Down Expand Up @@ -58,12 +59,12 @@ export class AddBoardModalComponent implements OnInit {

projects: Project[];
selectedProject = '';
selectedFile: File | null = null; // File to upload

constructor(
public dialogRef: MatDialogRef<AddBoardModalComponent>,
public UserService: UserService,
public userService: UserService,
// public fileUploadService: FileUploadService,
public fileUploadService: FileUploadService,
public fabricUtils: FabricUtils,
@Inject(MAT_DIALOG_DATA) public data: any
) {
Expand Down Expand Up @@ -108,12 +109,37 @@ export class AddBoardModalComponent implements OnInit {
this.tags = this.tags.filter((tag) => tag != tagRemove);
}

async compressFile() {
// const image = await this.fileUploadService.compressFile();
// this.bgImgURL = await this.fileUploadService.upload(image);
fabric.Image.fromURL(this.bgImgURL, async (image) => {
this.bgImgSettings = this.fabricUtils.createImageSettings(image);
});
uploadImage(): void {
const input = document.createElement('input');
input.type = 'file';
input.accept = 'image/*';
input.click();

input.onchange = () => {
const file = input.files?.[0];
if (file) {
this.fileUploadService.uploadImage(file).subscribe({
next: (response: any) => {
console.log('Image uploaded successfully', response);
this.bgImgURL =
environment.ckboardDomain + '/api/image/' + response.imageUrl;
fabric.Image.fromURL(this.bgImgURL, async (image) => {
this.bgImgSettings = this.fabricUtils.createImageSettings(image);
});
},
error: (error) => {
console.error('Image upload failed', error);
},
});
}
};
}

onFileSelected(event: Event) {
const fileInput = event.target as HTMLInputElement;
if (fileInput.files) {
this.selectedFile = fileInput.files[0]; // Get the selected file
}
}

handleDialogSubmit() {
Expand Down
Loading