Add comprehensive messenger features and improvements

- Backend improvements: flexible user authentication, nullable fields
- Frontend enhancements: new screens (NewMessage, UserInfo), improved UI/UX
- Chat functionality: real-time messaging with polling, message actions
- Visual upgrades: gradient backgrounds, better themes, modern design
- Added project documentation (CLAUDE.md) and development guidelines

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Bivekich
2025-08-06 21:42:36 +03:00
parent 5a3fdc01f8
commit bd731f1f57
17 changed files with 3063 additions and 621 deletions

View File

@ -12,7 +12,9 @@ async function bootstrap() {
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
});
await app.listen(process.env.PORT ?? 3000);
console.log(`Application is running on: http://localhost:${process.env.PORT ?? 3000}/graphql`);
const port = process.env.PORT ?? 3000;
await app.listen(port, '0.0.0.0');
console.log(`Application is running on: http://0.0.0.0:${port}/graphql`);
console.log(`For mobile devices use: http://192.168.31.210:${port}/graphql`);
}
bootstrap();

View File

@ -25,7 +25,7 @@ export class AuthService {
throw new UnauthorizedException('Неверный логин или пароль');
}
const payload = { username: user.username, sub: user.id };
const payload = { username: user.username || user.email, sub: user.id };
return {
access_token: this.jwtService.sign(payload),
user,
@ -34,7 +34,7 @@ export class AuthService {
async register(username: string, email: string, password: string) {
const user = await this.usersService.create(username, email, password);
const payload = { username: user.username, sub: user.id };
const payload = { username: user.username || user.email, sub: user.id };
return {
access_token: this.jwtService.sign(payload),
user,

View File

@ -9,13 +9,13 @@ export class User {
@PrimaryGeneratedColumn('uuid')
id: string;
@Field()
@Column({ unique: true })
username: string;
@Field(() => String, { nullable: true })
@Column({ unique: true, nullable: true })
username?: string;
@Field()
@Column({ unique: true })
email: string;
@Field(() => String, { nullable: true })
@Column({ unique: true, nullable: true })
email?: string;
@HideField()
@Column()
@ -29,9 +29,9 @@ export class User {
@Column({ nullable: true })
bio?: string;
@Field(() => Boolean)
@Column({ default: false })
isOnline: boolean;
@Field(() => Boolean, { nullable: true })
@Column({ default: false, nullable: true })
isOnline?: boolean;
@Field(() => Date, { nullable: true })
@Column({ nullable: true })

View File

@ -12,9 +12,13 @@ export class UsersService {
) {}
async create(username: string, email: string, password: string): Promise<User> {
const existingUser = await this.usersRepository.findOne({
where: [{ username }, { email }],
});
const whereConditions: Array<{ username?: string; email?: string }> = [];
if (username) whereConditions.push({ username });
if (email) whereConditions.push({ email });
const existingUser = whereConditions.length > 0 ? await this.usersRepository.findOne({
where: whereConditions,
}) : null;
if (existingUser) {
throw new ConflictException('Пользователь с таким username или email уже существует');
@ -43,10 +47,12 @@ export class UsersService {
}
async findByUsername(username: string): Promise<User | null> {
if (!username) return null;
return this.usersRepository.findOne({ where: { username } });
}
async findByEmail(email: string): Promise<User | null> {
if (!email) return null;
return this.usersRepository.findOne({ where: { email } });
}