Issues gitea

This commit is contained in:
2026-01-15 21:26:07 +01:00
parent 897ab1ff77
commit 09e7ce59a9
18 changed files with 496 additions and 124 deletions

View File

@@ -0,0 +1,119 @@
import { Pool } from 'pg';
import { drizzle, NodePgDatabase } from 'drizzle-orm/node-postgres';
import { sql, eq, and } from 'drizzle-orm';
import * as schema from '../src/drizzle/schema';
import { users_json } from '../src/drizzle/schema';
// Mock JwtUser
interface JwtUser {
email: string;
}
// Logic from UserService.addFavorite
async function addFavorite(db: NodePgDatabase<typeof schema>, id: string, user: JwtUser) {
console.log(`[Action] Adding favorite. Target ID: ${id}, Favoriter Email: ${user.email}`);
await db
.update(schema.users_json)
.set({
data: sql`jsonb_set(${schema.users_json.data}, '{favoritesForUser}',
coalesce((${schema.users_json.data}->'favoritesForUser')::jsonb, '[]'::jsonb) || to_jsonb(${user.email}::text))`,
} as any)
.where(eq(schema.users_json.id, id));
}
// Logic from UserService.getFavoriteUsers
async function getFavoriteUsers(db: NodePgDatabase<typeof schema>, user: JwtUser) {
console.log(`[Action] Fetching favorites for ${user.email}`);
// Corrected query using `?` operator (matches array element check)
const data = await db
.select()
.from(schema.users_json)
.where(sql`${schema.users_json.data}->'favoritesForUser' ? ${user.email}`);
return data;
}
// Logic from UserService.deleteFavorite
async function deleteFavorite(db: NodePgDatabase<typeof schema>, id: string, user: JwtUser) {
console.log(`[Action] Removing favorite. Target ID: ${id}, Favoriter Email: ${user.email}`);
await db
.update(schema.users_json)
.set({
data: sql`jsonb_set(${schema.users_json.data}, '{favoritesForUser}',
(SELECT coalesce(jsonb_agg(elem), '[]'::jsonb)
FROM jsonb_array_elements(coalesce(${schema.users_json.data}->'favoritesForUser', '[]'::jsonb)) AS elem
WHERE elem::text != to_jsonb(${user.email}::text)::text))`,
} as any)
.where(eq(schema.users_json.id, id));
}
async function main() {
console.log('═══════════════════════════════════════════════════════');
console.log(' FAVORITES REPRODUCTION SCRIPT');
console.log('═══════════════════════════════════════════════════════\n');
const connectionString = process.env.DATABASE_URL || 'postgresql://postgres:postgres@localhost:5432/bizmatch';
const pool = new Pool({ connectionString });
const db = drizzle(pool, { schema });
try {
// 1. Find a "professional" user to be the TARGET listing
// filtering by customerType = 'professional' inside the jsonb data
const targets = await db.select().from(users_json).limit(1);
if (targets.length === 0) {
console.error("No users found in DB to test with.");
return;
}
const targetUser = targets[0];
console.log(`Found target user: ID=${targetUser.id}, Email=${targetUser.email}`);
// 2. Define a "favoriter" user (doesn't need to exist in DB for the logic to work, but better if it's realistic)
// We'll just use a dummy email or one from DB if available.
const favoriterEmail = 'test-repro-favoriter@example.com';
const favoriter: JwtUser = { email: favoriterEmail };
// 3. Clear any existing favorite for this pair first
await deleteFavorite(db, targetUser.id, favoriter);
// 4. Add Favorite
await addFavorite(db, targetUser.id, favoriter);
// 5. Verify it was added by checking the raw data
const updatedTarget = await db.select().from(users_json).where(eq(users_json.id, targetUser.id));
const favoritesData = (updatedTarget[0].data as any).favoritesForUser;
console.log(`\n[Check] Raw favoritesForUser data on target:`, favoritesData);
if (!favoritesData || !favoritesData.includes(favoriterEmail)) {
console.error("❌ Add Favorite FAILED. Email not found in favoritesForUser array.");
} else {
console.log("✅ Add Favorite SUCCESS. Email found in JSON.");
}
// 6. Test retrieval using the getFavoriteUsers query
const retrievedFavorites = await getFavoriteUsers(db, favoriter);
console.log(`\n[Check] retrievedFavorites count: ${retrievedFavorites.length}`);
const found = retrievedFavorites.find(u => u.id === targetUser.id);
if (found) {
console.log("✅ Get Favorites SUCCESS. Target user returned in query.");
} else {
console.log("❌ Get Favorites FAILED. Target user NOT returned by query.");
console.log("Query used: favoritesForUser @> [email]");
}
// 7. Cleanup
await deleteFavorite(db, targetUser.id, favoriter);
console.log("\n[Cleanup] Removed test favorite.");
} catch (error) {
console.error('❌ Script failed:', error);
} finally {
await pool.end();
}
}
main();

View File

@@ -13,7 +13,13 @@ export class BusinessListingsController {
constructor(
private readonly listingsService: BusinessListingService,
@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger,
) {}
) { }
@UseGuards(AuthGuard)
@Post('favorites/all')
async findFavorites(@Request() req): Promise<any> {
return await this.listingsService.findFavoriteListings(req.user as JwtUser);
}
@UseGuards(OptionalAuthGuard)
@Get(':slugOrId')
@@ -21,11 +27,7 @@ export class BusinessListingsController {
// Support both slug (e.g., "restaurant-austin-tx-a3f7b2c1") and UUID
return await this.listingsService.findBusinessBySlugOrId(slugOrId, req.user as JwtUser);
}
@UseGuards(AuthGuard)
@Get('favorites/all')
async findFavorites(@Request() req): Promise<any> {
return await this.listingsService.findFavoriteListings(req.user as JwtUser);
}
@UseGuards(OptionalAuthGuard)
@Get('user/:userid')
async findByUserId(@Request() req, @Param('userid') userid: string): Promise<BusinessListing[]> {

View File

@@ -15,7 +15,13 @@ export class CommercialPropertyListingsController {
private readonly listingsService: CommercialPropertyService,
private fileService: FileService,
@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger,
) {}
) { }
@UseGuards(AuthGuard)
@Post('favorites/all')
async findFavorites(@Request() req): Promise<any> {
return await this.listingsService.findFavoriteListings(req.user as JwtUser);
}
@UseGuards(OptionalAuthGuard)
@Get(':slugOrId')
@@ -24,12 +30,6 @@ export class CommercialPropertyListingsController {
return await this.listingsService.findCommercialBySlugOrId(slugOrId, req.user as JwtUser);
}
@UseGuards(AuthGuard)
@Get('favorites/all')
async findFavorites(@Request() req): Promise<any> {
return await this.listingsService.findFavoriteListings(req.user as JwtUser);
}
@UseGuards(OptionalAuthGuard)
@Get('user/:email')
async findByEmail(@Request() req, @Param('email') email: string): Promise<CommercialPropertyListing[]> {

View File

@@ -6,6 +6,7 @@ import { UserService } from '../user/user.service';
import { BrokerListingsController } from './broker-listings.controller';
import { BusinessListingsController } from './business-listings.controller';
import { CommercialPropertyListingsController } from './commercial-property-listings.controller';
import { UserListingsController } from './user-listings.controller';
import { FirebaseAdminModule } from 'src/firebase-admin/firebase-admin.module';
import { GeoModule } from '../geo/geo.module';
@@ -16,7 +17,7 @@ import { UnknownListingsController } from './unknown-listings.controller';
@Module({
imports: [DrizzleModule, AuthModule, GeoModule,FirebaseAdminModule],
controllers: [BusinessListingsController, CommercialPropertyListingsController, UnknownListingsController, BrokerListingsController],
controllers: [BusinessListingsController, CommercialPropertyListingsController, UnknownListingsController, BrokerListingsController, UserListingsController],
providers: [BusinessListingService, CommercialPropertyService, FileService, UserService, BusinessListingService, CommercialPropertyService, GeoService],
exports: [BusinessListingService, CommercialPropertyService],
})

View File

@@ -0,0 +1,29 @@
import { Controller, Delete, Param, Post, Request, UseGuards } from '@nestjs/common';
import { AuthGuard } from '../jwt-auth/auth.guard';
import { JwtUser } from '../models/main.model';
import { UserService } from '../user/user.service';
@Controller('listings/user')
export class UserListingsController {
constructor(private readonly userService: UserService) { }
@UseGuards(AuthGuard)
@Post('favorite/:id')
async addFavorite(@Request() req, @Param('id') id: string) {
await this.userService.addFavorite(id, req.user as JwtUser);
return { success: true, message: 'Added to favorites' };
}
@UseGuards(AuthGuard)
@Delete('favorite/:id')
async deleteFavorite(@Request() req, @Param('id') id: string) {
await this.userService.deleteFavorite(id, req.user as JwtUser);
return { success: true, message: 'Removed from favorites' };
}
@UseGuards(AuthGuard)
@Post('favorites/all')
async getFavorites(@Request() req) {
return await this.userService.getFavoriteUsers(req.user as JwtUser);
}
}

View File

@@ -0,0 +1,57 @@
import { drizzle } from 'drizzle-orm/node-postgres';
import { Client } from 'pg';
import * as schema from '../drizzle/schema';
import { sql } from 'drizzle-orm';
import * as dotenv from 'dotenv';
dotenv.config();
const client = new Client({
connectionString: process.env.PG_CONNECTION,
});
async function main() {
await client.connect();
const db = drizzle(client, { schema });
const testEmail = 'knuth.timo@gmail.com';
const targetEmail = 'target.user@example.com';
console.log('--- Starting Debug Script ---');
// 1. Simulate finding a user to favorite (using a dummy or existing one)
// For safety, let's just query existing users to see if any have favorites set
const usersWithFavorites = await db.select({
id: schema.users_json.id,
email: schema.users_json.email,
favorites: sql`${schema.users_json.data}->'favoritesForUser'`
}).from(schema.users_json);
console.log(`Found ${usersWithFavorites.length} users.`);
const usersWithAnyFavorites = usersWithFavorites.filter(u => u.favorites !== null);
console.log(`Users with 'favoritesForUser' field:`, JSON.stringify(usersWithAnyFavorites, null, 2));
// 2. Test the specific WHERE clause used in the service
// .where(sql`${schema.users_json.data}->'favoritesForUser' @> ${JSON.stringify([user.email])}::jsonb`);
console.log(`Testing query for email: ${testEmail}`);
try {
const result = await db
.select({
id: schema.users_json.id,
email: schema.users_json.email
})
.from(schema.users_json)
.where(sql`${schema.users_json.data}->'favoritesForUser' @> ${JSON.stringify([testEmail])}::jsonb`);
console.log('Query Result:', result);
} catch (e) {
console.error('Query Failed:', e);
}
await client.end();
}
main().catch(console.error);

View File

@@ -19,7 +19,7 @@ export class UserService {
@Inject(PG_CONNECTION) private conn: NodePgDatabase<typeof schema>,
private fileService: FileService,
private geoService: GeoService,
) {}
) { }
private getWhereConditions(criteria: UserListingCriteria): SQL[] {
const whereConditions: SQL[] = [];
@@ -158,4 +158,34 @@ export class UserService {
throw error;
}
}
async addFavorite(id: string, user: JwtUser): Promise<void> {
await this.conn
.update(schema.users_json)
.set({
data: sql`jsonb_set(${schema.users_json.data}, '{favoritesForUser}',
coalesce((${schema.users_json.data}->'favoritesForUser')::jsonb, '[]'::jsonb) || to_jsonb(${user.email}::text))`,
} as any)
.where(eq(schema.users_json.id, id));
}
async deleteFavorite(id: string, user: JwtUser): Promise<void> {
await this.conn
.update(schema.users_json)
.set({
data: sql`jsonb_set(${schema.users_json.data}, '{favoritesForUser}',
(SELECT coalesce(jsonb_agg(elem), '[]'::jsonb)
FROM jsonb_array_elements(coalesce(${schema.users_json.data}->'favoritesForUser', '[]'::jsonb)) AS elem
WHERE elem::text != to_jsonb(${user.email}::text)::text))`,
} as any)
.where(eq(schema.users_json.id, id));
}
async getFavoriteUsers(user: JwtUser): Promise<User[]> {
const data = await this.conn
.select()
.from(schema.users_json)
.where(sql`${schema.users_json.data}->'favoritesForUser' ? ${user.email}`);
return data.map(u => ({ id: u.id, email: u.email, ...(u.data as User) }) as User);
}
}