Skip to content

Commit

Permalink
Merge pull request #599 from VitNode/feat/user_settings_overview
Browse files Browse the repository at this point in the history
feat: Add overview view in user settings
  • Loading branch information
aXenDeveloper authored Dec 7, 2024
2 parents 605bd4e + 2465969 commit eda4721
Show file tree
Hide file tree
Showing 82 changed files with 1,901 additions and 1,812 deletions.
19 changes: 9 additions & 10 deletions apps/backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,17 @@
"db": "vitnode-backend db"
},
"dependencies": {
"@nestjs/common": "^10.4.12",
"@nestjs/core": "^10.4.12",
"@nestjs/platform-express": "^10.4.12",
"@nestjs/common": "^10.4.13",
"@nestjs/core": "^10.4.13",
"@nestjs/platform-express": "^10.4.13",
"@nestjs/schedule": "^4.1.1",
"@nestjs/throttler": "^6.2.1",
"@react-email/components": "^0.0.28",
"@react-email/components": "^0.0.29",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.1",
"drizzle-kit": "^0.28.1",
"drizzle-orm": "^0.36.4",
"react": "19.0.0-rc.1",
"react-dom": "19.0.0-rc.1",
"drizzle-kit": "^0.29.1",
"drizzle-orm": "^0.37.0",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"reflect-metadata": "^0.2.2",
"vitnode-backend": "workspace:*",
"vitnode-backend-ai-google": "workspace:*",
Expand All @@ -38,7 +37,7 @@
"@swc/cli": "^0.5.2",
"@types/express": "^5.0.0",
"@types/node": "^22.10.1",
"@types/react": "^18.3.12",
"@types/react": "^19.0.0",
"cross-env": "^7.0.3",
"eslint-config-typescript-vitnode": "workspace:*",
"shared": "workspace:*",
Expand Down
14 changes: 7 additions & 7 deletions apps/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,28 +16,28 @@
"dependencies": {
"@hookform/resolvers": "^3.9.1",
"geist": "^1.3.1",
"lucide-react": "^0.462.0",
"lucide-react": "^0.468.0",
"next": "^15.0.3",
"next-intl": "^3.25.3",
"react": "19.0.0-rc.1",
"react-dom": "19.0.0-rc.1",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-hook-form": "^7.53.2",
"recharts": "^2.13.3",
"recharts": "^2.14.1",
"sonner": "^1.7.0",
"vitnode-frontend": "workspace:*",
"zod": "^3.23.8"
},
"devDependencies": {
"@next/bundle-analyzer": "^15.0.3",
"@types/node": "^22.10.1",
"@types/react": "^18.3.12",
"@types/react-dom": "^18.3.1",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"autoprefixer": "^10.4.20",
"cross-env": "^7.0.3",
"eslint-config-typescript-vitnode": "workspace:*",
"postcss": "^8.4.49",
"shared": "workspace:*",
"tailwindcss": "^3.4.15",
"tailwindcss": "^3.4.16",
"typescript": "^5.7.2",
"vitnode-shared": "workspace:*"
}
Expand Down
33 changes: 15 additions & 18 deletions apps/frontend/src/plugins/core/langs/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -182,28 +182,25 @@
},
"settings": {
"title": "Settings",
"desc": "Manage your account settings.",
"open_sheet": "Open Menu",
"open_sheet_desc": "You can change your account settings here.",
"change_avatar": {
"title": "Change Avatar",
"desc": "Choose one option to change your avatar.",
"options": {
"upload": {
"title": "Upload a new avatar",
"success": "Your avatar has been changed.",
"error": "An error occurred while uploading your avatar."
},
"delete": {
"title": "Delete avatar",
"success": "Your avatar has been deleted.",
"error": "An error occurred while deleting your avatar."
"overview": {
"title": "Overview",
"change_avatar": {
"title": "Change Avatar",
"desc": "Show your personality with a new avatar.",
"submit": "Change Avatar",
"success": "Your avatar has been changed.",
"types": {
"upload": "Upload a new avatar",
"delete": "Delete avatar"
}
},
"submit": "Change Avatar"
},
"overview": {
"title": "Overview"
"basic_info": {
"title": "Basic Information",
"email": "Email",
"joined_at": "Joined at"
}
},
"files": {
"title": "My Files",
Expand Down
4 changes: 2 additions & 2 deletions apps/shared/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
}
},
"dependencies": {
"@nestjs/common": "^10.4.12",
"@nestjs/swagger": "^8.0.7"
"@nestjs/common": "^10.4.13",
"@nestjs/swagger": "^8.1.0"
},
"devDependencies": {
"@types/multer": "^1.4.12",
Expand Down
4 changes: 2 additions & 2 deletions apps/shared/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
"extends": "eslint-config-typescript-vitnode/tsconfig.nest.json",
"compilerOptions": {
"target": "ESNext",
"module": "es2015",
"moduleResolution": "bundler",
"module": "Node16",
"moduleResolution": "Node16",
"outDir": "./dist",
"rootDir": "./plugins",
"esModuleInterop": true,
Expand Down
37 changes: 24 additions & 13 deletions packages/backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,40 +69,51 @@
"express",
"codegen"
],
"peerDependencies": {
"@nestjs/common": "^10.4.13",
"@nestjs/core": "^10.4.13",
"@nestjs/platform-express": "^10.4.13",
"@nestjs/schedule": "^4.1.1",
"@react-email/components": "^0.0.29",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.1",
"react": "^19.0.0",
"react-dom": "^19.0.0"
},
"devDependencies": {
"@nestjs/common": "^10.4.12",
"@nestjs/platform-express": "^10.4.12",
"@nestjs/common": "^10.4.13",
"@nestjs/core": "^10.4.13",
"@nestjs/platform-express": "^10.4.13",
"@nestjs/schedule": "^4.1.1",
"@react-email/components": "^0.0.28",
"@react-email/components": "^0.0.29",
"@swc/cli": "^0.5.2",
"@swc/core": "^1.9.3",
"@swc/core": "^1.10.0",
"@types/cookie-parser": "^1.4.8",
"@types/express": "^5.0.0",
"@types/multer": "^1.4.12",
"@types/node": "^22.10.1",
"@types/pg": "^8.11.10",
"@types/react": "^18.3.12",
"@types/react": "^19.0.0",
"@types/ua-parser-js": "^0.7.39",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.1",
"concurrently": "^9.1.0",
"drizzle-kit": "^0.29.1",
"drizzle-orm": "^0.37.0",
"eslint-config-typescript-vitnode": "workspace:*",
"react": "19.0.0-rc.1",
"react-dom": "19.0.0-rc.1",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"tsup": "^8.3.5",
"typescript": "^5.7.2"
},
"dependencies": {
"@nestjs/config": "^3.3.0",
"@nestjs/core": "^10.4.12",
"@nestjs/jwt": "^10.2.0",
"@nestjs/serve-static": "^4.0.2",
"@nestjs/swagger": "^8.0.7",
"@react-email/render": "^1.0.2",
"@nestjs/swagger": "^8.1.0",
"@react-email/render": "^1.0.3",
"cookie-parser": "^1.4.7",
"dotenv": "^16.4.5",
"drizzle-kit": "^0.28.1",
"drizzle-orm": "^0.36.4",
"dotenv": "^16.4.7",
"helmet": "^8.0.0",
"pg": "^8.13.1",
"rxjs": "^7.8.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export class SignInAuthService {
language: true,
name_seo: true,
avatar_color: true,
joined_at: true,
},
});
if (!user?.password) {
Expand Down
7 changes: 6 additions & 1 deletion packages/backend/src/core/auth/settings/settings.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,13 @@ import { Module } from '@nestjs/common';

import { DevicesSettingsAuthModule } from './devices/devices.module';
import { FilesSettingsAuthModule } from './files/files.module';
import { UserSettingsAuthModule } from './user/user.module';

@Module({
imports: [DevicesSettingsAuthModule, FilesSettingsAuthModule],
imports: [
DevicesSettingsAuthModule,
FilesSettingsAuthModule,
UserSettingsAuthModule,
],
})
export class SettingsAuthModule {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { core_files_avatars } from '@/database/schema/users';
import { FilesHelperService } from '@/helpers/files/files-helper.service';
import { InternalDatabaseService } from '@/utils/database/internal_database.service';
import { BadRequestException, Injectable } from '@nestjs/common';
import { eq } from 'drizzle-orm';
import { UploadAvatarUserSettingsAuthBody } from 'vitnode-shared/auth/settings/user.dto';
import { User } from 'vitnode-shared/user.dto';

@Injectable()
export class UploadAvatarUserSettingsAuthService {
constructor(
private readonly databaseService: InternalDatabaseService,
private readonly filesHelper: FilesHelperService,
) {}

async uploadAvatar({
body: { delete_avatar },
currentUser,
files: { avatar },
}: {
body: Omit<UploadAvatarUserSettingsAuthBody, 'avatar'>;
currentUser: User;
files: Pick<UploadAvatarUserSettingsAuthBody, 'avatar'>;
}): Promise<void> {
if (!delete_avatar && !avatar) {
throw new BadRequestException('No avatar provided');
}

const avatarFromDB =
await this.databaseService.db.query.core_files_avatars.findFirst({
where: (table, { eq }) => eq(table.user_id, currentUser.id),
});

if (avatarFromDB || (delete_avatar && avatarFromDB)) {
await this.filesHelper.delete({
dir_folder: avatarFromDB.dir_folder,
file_name: avatarFromDB.file_name,
});

await this.databaseService.db
.delete(core_files_avatars)
.where(eq(core_files_avatars.user_id, currentUser.id));

if (delete_avatar) return;
}

if (!avatar) {
throw new BadRequestException('No avatar provided');
}

const file = await this.filesHelper.upload({
file: avatar,
folder: 'avatars',
plugin_code: 'core',
});

await this.databaseService.db.insert(core_files_avatars).values({
...file,
user_id: currentUser.id,
});
}
}
51 changes: 51 additions & 0 deletions packages/backend/src/core/auth/settings/user/user.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { Controllers } from '@/helpers/controller.decorator';
import { FilesValidationPipe } from '@/helpers/files/files.pipe';
import { UploadFilesMethod } from '@/helpers/upload-files.decorator';
import { CurrentUser } from '@/helpers/user.decorator';
import { Body, Put, UploadedFiles } from '@nestjs/common';
import { ApiOkResponse } from '@nestjs/swagger';
import { UploadAvatarUserSettingsAuthBody } from 'vitnode-shared/auth/settings/user.dto';
import { User } from 'vitnode-shared/user.dto';

import { UploadAvatarUserSettingsAuthService } from './services/upload_avatar.service';

@Controllers({
plugin_name: 'Core',
plugin_code: 'core',
route: 'auth/settings/user',
isProtect: true,
})
export class UserSettingsAuthController {
constructor(
private readonly uploadAvatarService: UploadAvatarUserSettingsAuthService,
) {}

@ApiOkResponse({
description: 'Upload or delete avatar',
})
@Put('avatar')
@UploadFilesMethod({
fields: ['avatar'],
})
async uploadAvatar(
@UploadedFiles(
new FilesValidationPipe({
avatar: {
maxSize: 1024 * 1024 * 2, // 2 MB
acceptMimeType: ['image/png', 'image/jpeg', 'image/webp'],
isOptional: true,
maxCount: 1,
},
}),
)
files: Pick<UploadAvatarUserSettingsAuthBody, 'avatar'>,
@Body() body: UploadAvatarUserSettingsAuthBody,
@CurrentUser() currentUser: User,
): Promise<void> {
await this.uploadAvatarService.uploadAvatar({
body,
files,
currentUser,
});
}
}
10 changes: 10 additions & 0 deletions packages/backend/src/core/auth/settings/user/user.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Module } from '@nestjs/common';

import { UploadAvatarUserSettingsAuthService } from './services/upload_avatar.service';
import { UserSettingsAuthController } from './user.controller';

@Module({
providers: [UploadAvatarUserSettingsAuthService],
controllers: [UserSettingsAuthController],
})
export class UserSettingsAuthModule {}
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ export class CallbackSSOAuthService {
language: true,
name_seo: true,
avatar_color: true,
joined_at: true,
},
},
},
Expand Down Expand Up @@ -98,6 +99,7 @@ export class CallbackSSOAuthService {
name_seo: true,
avatar_color: true,
email_verified: true,
joined_at: true,
},
});
// If user exists, create SSO token and sign in
Expand Down Expand Up @@ -146,6 +148,7 @@ export class CallbackSSOAuthService {
avatar_color: '',
provider,
provider_id: data.id,
joined_at: new Date(),
};
}
}
Loading

0 comments on commit eda4721

Please sign in to comment.