Agent 2 - Database Integration (P0 Critical): - Add Prisma ORM with PostgreSQL for persistent data storage - Create comprehensive database schema with 20+ models: - User & authentication models - Plant & lineage tracking - Transport events & supply chain - Vertical farming (farms, zones, batches, recipes) - Demand & market matching - Audit logging & blockchain storage - Implement complete database service layer (lib/db/): - users.ts: User CRUD with search and stats - plants.ts: Plant operations with lineage tracking - transport.ts: Transport events and carbon tracking - farms.ts: Vertical farm and crop batch management - demand.ts: Consumer preferences and market matching - audit.ts: Audit logging and blockchain integrity - Add PlantChainDB for database-backed blockchain - Create development seed script with sample data - Add database documentation (docs/DATABASE.md) - Update package.json with Prisma dependencies and scripts This provides the foundation for all other agents to build upon with persistent, scalable data storage.
199 lines
4.6 KiB
TypeScript
199 lines
4.6 KiB
TypeScript
/**
|
|
* User Database Service
|
|
* CRUD operations for users
|
|
*/
|
|
|
|
import prisma from './prisma';
|
|
import type { User, UserType, Prisma } from '@prisma/client';
|
|
import type { PaginationOptions, PaginatedResult } from './types';
|
|
import { createPaginatedResult } from './types';
|
|
|
|
// Create a new user
|
|
export async function createUser(data: {
|
|
email: string;
|
|
name: string;
|
|
walletAddress?: string;
|
|
passwordHash?: string;
|
|
avatarUrl?: string;
|
|
bio?: string;
|
|
latitude?: number;
|
|
longitude?: number;
|
|
address?: string;
|
|
city?: string;
|
|
country?: string;
|
|
userType?: UserType;
|
|
}): Promise<User> {
|
|
return prisma.user.create({
|
|
data: {
|
|
...data,
|
|
userType: data.userType || 'CONSUMER',
|
|
},
|
|
});
|
|
}
|
|
|
|
// Get user by ID
|
|
export async function getUserById(id: string): Promise<User | null> {
|
|
return prisma.user.findUnique({
|
|
where: { id },
|
|
});
|
|
}
|
|
|
|
// Get user by email
|
|
export async function getUserByEmail(email: string): Promise<User | null> {
|
|
return prisma.user.findUnique({
|
|
where: { email },
|
|
});
|
|
}
|
|
|
|
// Get user by wallet address
|
|
export async function getUserByWalletAddress(walletAddress: string): Promise<User | null> {
|
|
return prisma.user.findUnique({
|
|
where: { walletAddress },
|
|
});
|
|
}
|
|
|
|
// Update user
|
|
export async function updateUser(
|
|
id: string,
|
|
data: Prisma.UserUpdateInput
|
|
): Promise<User> {
|
|
return prisma.user.update({
|
|
where: { id },
|
|
data,
|
|
});
|
|
}
|
|
|
|
// Delete user
|
|
export async function deleteUser(id: string): Promise<User> {
|
|
return prisma.user.delete({
|
|
where: { id },
|
|
});
|
|
}
|
|
|
|
// Get all users with pagination
|
|
export async function getUsers(
|
|
options: PaginationOptions = {},
|
|
filters?: {
|
|
userType?: UserType;
|
|
city?: string;
|
|
country?: string;
|
|
}
|
|
): Promise<PaginatedResult<User>> {
|
|
const page = options.page || 1;
|
|
const limit = options.limit || 20;
|
|
const skip = (page - 1) * limit;
|
|
|
|
const where: Prisma.UserWhereInput = {};
|
|
if (filters?.userType) where.userType = filters.userType;
|
|
if (filters?.city) where.city = { contains: filters.city, mode: 'insensitive' };
|
|
if (filters?.country) where.country = { contains: filters.country, mode: 'insensitive' };
|
|
|
|
const [users, total] = await Promise.all([
|
|
prisma.user.findMany({
|
|
where,
|
|
skip,
|
|
take: limit,
|
|
orderBy: { createdAt: 'desc' },
|
|
}),
|
|
prisma.user.count({ where }),
|
|
]);
|
|
|
|
return createPaginatedResult(users, total, page, limit);
|
|
}
|
|
|
|
// Get users by type
|
|
export async function getUsersByType(userType: UserType): Promise<User[]> {
|
|
return prisma.user.findMany({
|
|
where: { userType },
|
|
orderBy: { name: 'asc' },
|
|
});
|
|
}
|
|
|
|
// Update last login
|
|
export async function updateLastLogin(id: string): Promise<User> {
|
|
return prisma.user.update({
|
|
where: { id },
|
|
data: { lastLoginAt: new Date() },
|
|
});
|
|
}
|
|
|
|
// Search users
|
|
export async function searchUsers(
|
|
query: string,
|
|
options: PaginationOptions = {}
|
|
): Promise<PaginatedResult<User>> {
|
|
const page = options.page || 1;
|
|
const limit = options.limit || 20;
|
|
const skip = (page - 1) * limit;
|
|
|
|
const where: Prisma.UserWhereInput = {
|
|
OR: [
|
|
{ name: { contains: query, mode: 'insensitive' } },
|
|
{ email: { contains: query, mode: 'insensitive' } },
|
|
{ city: { contains: query, mode: 'insensitive' } },
|
|
],
|
|
};
|
|
|
|
const [users, total] = await Promise.all([
|
|
prisma.user.findMany({
|
|
where,
|
|
skip,
|
|
take: limit,
|
|
orderBy: { name: 'asc' },
|
|
}),
|
|
prisma.user.count({ where }),
|
|
]);
|
|
|
|
return createPaginatedResult(users, total, page, limit);
|
|
}
|
|
|
|
// Get user with their plants
|
|
export async function getUserWithPlants(id: string) {
|
|
return prisma.user.findUnique({
|
|
where: { id },
|
|
include: {
|
|
ownedPlants: {
|
|
orderBy: { registeredAt: 'desc' },
|
|
},
|
|
},
|
|
});
|
|
}
|
|
|
|
// Get user with their farms
|
|
export async function getUserWithFarms(id: string) {
|
|
return prisma.user.findUnique({
|
|
where: { id },
|
|
include: {
|
|
verticalFarms: {
|
|
include: {
|
|
zones: true,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
}
|
|
|
|
// Get user statistics
|
|
export async function getUserStats(id: string) {
|
|
const [
|
|
plantCount,
|
|
farmCount,
|
|
transportEventsSent,
|
|
transportEventsReceived,
|
|
supplyCommitments,
|
|
] = await Promise.all([
|
|
prisma.plant.count({ where: { ownerId: id } }),
|
|
prisma.verticalFarm.count({ where: { ownerId: id } }),
|
|
prisma.transportEvent.count({ where: { senderId: id } }),
|
|
prisma.transportEvent.count({ where: { receiverId: id } }),
|
|
prisma.supplyCommitment.count({ where: { growerId: id } }),
|
|
]);
|
|
|
|
return {
|
|
plantCount,
|
|
farmCount,
|
|
transportEventsSent,
|
|
transportEventsReceived,
|
|
supplyCommitments,
|
|
};
|
|
}
|