clhq-postgres-module 1.1.0-alpha.106 → 1.1.0-alpha.107
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/repositories/ai-embedding.repository.d.ts +38 -0
- package/dist/repositories/ai-embedding.repository.js +146 -0
- package/dist/repositories/asset-metadata.repository.d.ts +39 -0
- package/dist/repositories/asset-metadata.repository.js +195 -0
- package/dist/repositories/index.d.ts +5 -0
- package/dist/repositories/index.js +11 -1
- package/dist/repositories/metadata-activity-log.repository.d.ts +41 -0
- package/dist/repositories/metadata-activity-log.repository.js +220 -0
- package/dist/repositories/project-metadata.repository.d.ts +32 -0
- package/dist/repositories/project-metadata.repository.js +186 -0
- package/dist/repositories/repository.module.js +10 -0
- package/dist/repositories/user-context.repository.d.ts +66 -0
- package/dist/repositories/user-context.repository.js +161 -0
- package/package.json +1 -1
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Repository } from 'typeorm';
|
|
2
|
+
import { BaseRepository } from './base.repository';
|
|
3
|
+
import { AiEmbedding, EmbeddingEntityType, EmbeddingType } from '../entities/ai-embedding.entity';
|
|
4
|
+
export declare class AiEmbeddingRepository extends BaseRepository<AiEmbedding> {
|
|
5
|
+
private readonly aiEmbeddingRepository;
|
|
6
|
+
constructor(aiEmbeddingRepository: Repository<AiEmbedding>);
|
|
7
|
+
findByEntity(entityType: EmbeddingEntityType, entityId: string): Promise<AiEmbedding | null>;
|
|
8
|
+
findByEntityType(entityType: EmbeddingEntityType, userId?: string): Promise<AiEmbedding[]>;
|
|
9
|
+
findByUserId(userId: string): Promise<AiEmbedding[]>;
|
|
10
|
+
findByWorkspaceId(workspaceId: string): Promise<AiEmbedding[]>;
|
|
11
|
+
upsertEmbedding(data: {
|
|
12
|
+
entityType: EmbeddingEntityType;
|
|
13
|
+
entityId: string;
|
|
14
|
+
userId?: string;
|
|
15
|
+
workspaceId?: string;
|
|
16
|
+
embeddingType?: EmbeddingType;
|
|
17
|
+
embedding: number[];
|
|
18
|
+
sourceText?: string;
|
|
19
|
+
modelId: string;
|
|
20
|
+
modelVersion?: string;
|
|
21
|
+
metadata?: AiEmbedding['metadata'];
|
|
22
|
+
}): Promise<AiEmbedding>;
|
|
23
|
+
deleteByEntity(entityType: EmbeddingEntityType, entityId: string): Promise<boolean>;
|
|
24
|
+
findSimilar(embedding: number[], entityType: EmbeddingEntityType, userId?: string, limit?: number, excludeEntityId?: string): Promise<Array<AiEmbedding & {
|
|
25
|
+
similarity: number;
|
|
26
|
+
}>>;
|
|
27
|
+
batchFindSimilar(queries: Array<{
|
|
28
|
+
embedding: number[];
|
|
29
|
+
entityType: EmbeddingEntityType;
|
|
30
|
+
userId?: string;
|
|
31
|
+
limit?: number;
|
|
32
|
+
}>): Promise<Array<Array<AiEmbedding & {
|
|
33
|
+
similarity: number;
|
|
34
|
+
}>>>;
|
|
35
|
+
getCountsByEntityType(userId?: string): Promise<Record<string, number>>;
|
|
36
|
+
findByModelVersion(modelId: string, modelVersion?: string): Promise<AiEmbedding[]>;
|
|
37
|
+
findOutdatedEmbeddings(modelId: string, currentVersion: string, limit?: number): Promise<AiEmbedding[]>;
|
|
38
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.AiEmbeddingRepository = void 0;
|
|
16
|
+
const typeorm_1 = require("typeorm");
|
|
17
|
+
const common_1 = require("@nestjs/common");
|
|
18
|
+
const typeorm_2 = require("@nestjs/typeorm");
|
|
19
|
+
const base_repository_1 = require("./base.repository");
|
|
20
|
+
const ai_embedding_entity_1 = require("../entities/ai-embedding.entity");
|
|
21
|
+
let AiEmbeddingRepository = class AiEmbeddingRepository extends base_repository_1.BaseRepository {
|
|
22
|
+
constructor(aiEmbeddingRepository) {
|
|
23
|
+
super(aiEmbeddingRepository);
|
|
24
|
+
this.aiEmbeddingRepository = aiEmbeddingRepository;
|
|
25
|
+
}
|
|
26
|
+
async findByEntity(entityType, entityId) {
|
|
27
|
+
return this.aiEmbeddingRepository.findOne({
|
|
28
|
+
where: { entityType, entityId },
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
async findByEntityType(entityType, userId) {
|
|
32
|
+
const where = { entityType };
|
|
33
|
+
if (userId) {
|
|
34
|
+
where.userId = userId;
|
|
35
|
+
}
|
|
36
|
+
return this.aiEmbeddingRepository.find({
|
|
37
|
+
where,
|
|
38
|
+
order: { createdAt: 'DESC' },
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
async findByUserId(userId) {
|
|
42
|
+
return this.aiEmbeddingRepository.find({
|
|
43
|
+
where: { userId },
|
|
44
|
+
order: { createdAt: 'DESC' },
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
async findByWorkspaceId(workspaceId) {
|
|
48
|
+
return this.aiEmbeddingRepository.find({
|
|
49
|
+
where: { workspaceId },
|
|
50
|
+
order: { createdAt: 'DESC' },
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
async upsertEmbedding(data) {
|
|
54
|
+
const existing = await this.findByEntity(data.entityType, data.entityId);
|
|
55
|
+
if (existing) {
|
|
56
|
+
await this.aiEmbeddingRepository.update(existing.id, {
|
|
57
|
+
embedding: data.embedding,
|
|
58
|
+
embeddingType: data.embeddingType || existing.embeddingType,
|
|
59
|
+
dimension: data.embedding.length,
|
|
60
|
+
sourceText: data.sourceText,
|
|
61
|
+
modelId: data.modelId,
|
|
62
|
+
modelVersion: data.modelVersion,
|
|
63
|
+
metadata: data.metadata,
|
|
64
|
+
});
|
|
65
|
+
return (await this.findById(existing.id));
|
|
66
|
+
}
|
|
67
|
+
return this.create({
|
|
68
|
+
...data,
|
|
69
|
+
dimension: data.embedding.length,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
async deleteByEntity(entityType, entityId) {
|
|
73
|
+
const result = await this.aiEmbeddingRepository.delete({
|
|
74
|
+
entityType,
|
|
75
|
+
entityId,
|
|
76
|
+
});
|
|
77
|
+
return (result.affected ?? 0) > 0;
|
|
78
|
+
}
|
|
79
|
+
async findSimilar(embedding, entityType, userId, limit = 20, excludeEntityId) {
|
|
80
|
+
const embeddingStr = JSON.stringify(embedding);
|
|
81
|
+
const dimension = embedding.length;
|
|
82
|
+
let query = this.aiEmbeddingRepository
|
|
83
|
+
.createQueryBuilder('ae')
|
|
84
|
+
.select('ae.*')
|
|
85
|
+
.addSelect(`1 - (ae.embedding::vector(${dimension}) <=> '${embeddingStr}'::vector(${dimension}))`, 'similarity')
|
|
86
|
+
.where('ae.entity_type = :entityType', { entityType })
|
|
87
|
+
.andWhere('ae.dimension = :dimension', { dimension });
|
|
88
|
+
if (userId) {
|
|
89
|
+
query = query.andWhere('ae.user_id = :userId', { userId });
|
|
90
|
+
}
|
|
91
|
+
if (excludeEntityId) {
|
|
92
|
+
query = query.andWhere('ae.entity_id != :excludeEntityId', {
|
|
93
|
+
excludeEntityId,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
const results = await query
|
|
97
|
+
.orderBy('similarity', 'DESC')
|
|
98
|
+
.limit(limit)
|
|
99
|
+
.getRawMany();
|
|
100
|
+
return results;
|
|
101
|
+
}
|
|
102
|
+
async batchFindSimilar(queries) {
|
|
103
|
+
const results = await Promise.all(queries.map((query) => this.findSimilar(query.embedding, query.entityType, query.userId, query.limit || 10)));
|
|
104
|
+
return results;
|
|
105
|
+
}
|
|
106
|
+
async getCountsByEntityType(userId) {
|
|
107
|
+
const queryBuilder = this.aiEmbeddingRepository
|
|
108
|
+
.createQueryBuilder('ae')
|
|
109
|
+
.select('ae.entity_type', 'entityType')
|
|
110
|
+
.addSelect('COUNT(*)', 'count')
|
|
111
|
+
.groupBy('ae.entity_type');
|
|
112
|
+
if (userId) {
|
|
113
|
+
queryBuilder.where('ae.user_id = :userId', { userId });
|
|
114
|
+
}
|
|
115
|
+
const results = await queryBuilder.getRawMany();
|
|
116
|
+
return results.reduce((acc, row) => {
|
|
117
|
+
acc[row.entityType] = parseInt(row.count, 10);
|
|
118
|
+
return acc;
|
|
119
|
+
}, {});
|
|
120
|
+
}
|
|
121
|
+
async findByModelVersion(modelId, modelVersion) {
|
|
122
|
+
const where = { modelId };
|
|
123
|
+
if (modelVersion) {
|
|
124
|
+
where.modelVersion = modelVersion;
|
|
125
|
+
}
|
|
126
|
+
return this.aiEmbeddingRepository.find({
|
|
127
|
+
where,
|
|
128
|
+
order: { createdAt: 'ASC' },
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
async findOutdatedEmbeddings(modelId, currentVersion, limit = 100) {
|
|
132
|
+
return this.aiEmbeddingRepository
|
|
133
|
+
.createQueryBuilder('ae')
|
|
134
|
+
.where('ae.model_id = :modelId', { modelId })
|
|
135
|
+
.andWhere('(ae.model_version IS NULL OR ae.model_version != :currentVersion)', { currentVersion })
|
|
136
|
+
.orderBy('ae.created_at', 'ASC')
|
|
137
|
+
.limit(limit)
|
|
138
|
+
.getMany();
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
exports.AiEmbeddingRepository = AiEmbeddingRepository;
|
|
142
|
+
exports.AiEmbeddingRepository = AiEmbeddingRepository = __decorate([
|
|
143
|
+
(0, common_1.Injectable)(),
|
|
144
|
+
__param(0, (0, typeorm_2.InjectRepository)(ai_embedding_entity_1.AiEmbedding)),
|
|
145
|
+
__metadata("design:paramtypes", [typeorm_1.Repository])
|
|
146
|
+
], AiEmbeddingRepository);
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Repository } from 'typeorm';
|
|
2
|
+
import { BaseRepository } from './base.repository';
|
|
3
|
+
import { AssetMetadata, AssetMetadataStatus } from '../entities/asset-metadata.entity';
|
|
4
|
+
export declare class AssetMetadataRepository extends BaseRepository<AssetMetadata> {
|
|
5
|
+
private readonly assetMetadataRepository;
|
|
6
|
+
constructor(assetMetadataRepository: Repository<AssetMetadata>);
|
|
7
|
+
findByAssetId(assetId: string): Promise<AssetMetadata | null>;
|
|
8
|
+
findByUserId(userId: string): Promise<AssetMetadata[]>;
|
|
9
|
+
findByWorkspaceId(workspaceId: string): Promise<AssetMetadata[]>;
|
|
10
|
+
findByStatus(status: AssetMetadataStatus): Promise<AssetMetadata[]>;
|
|
11
|
+
findPendingForProcessing(limit?: number): Promise<AssetMetadata[]>;
|
|
12
|
+
updateStatus(id: string, status: AssetMetadataStatus, error?: string): Promise<AssetMetadata | null>;
|
|
13
|
+
updateEmbedding(id: string, embedding: number[], model: string, version?: string): Promise<AssetMetadata | null>;
|
|
14
|
+
updateContent(id: string, content: AssetMetadata['content']): Promise<AssetMetadata | null>;
|
|
15
|
+
updateTechnical(id: string, technical: AssetMetadata['technical']): Promise<AssetMetadata | null>;
|
|
16
|
+
findSimilarByEmbedding(embedding: number[], userId: string, limit?: number, excludeAssetId?: string, minSimilarity?: number): Promise<Array<AssetMetadata & {
|
|
17
|
+
similarity: number;
|
|
18
|
+
}>>;
|
|
19
|
+
findSimilarByEmbeddingOptimized(embedding: number[], userId: string, limit?: number, minSimilarity?: number): Promise<Array<{
|
|
20
|
+
assetId: string;
|
|
21
|
+
similarity: number;
|
|
22
|
+
category: string;
|
|
23
|
+
tags: string[];
|
|
24
|
+
description: string;
|
|
25
|
+
usageCount: number;
|
|
26
|
+
lastUsedAt: Date;
|
|
27
|
+
}>>;
|
|
28
|
+
findByTags(userId: string, tags: string[]): Promise<AssetMetadata[]>;
|
|
29
|
+
searchByKeywords(userId: string, searchTerm: string): Promise<AssetMetadata[]>;
|
|
30
|
+
incrementUsageCount(assetId: string, projectId?: string): Promise<void>;
|
|
31
|
+
findWithoutEmbeddings(limit?: number): Promise<AssetMetadata[]>;
|
|
32
|
+
getStatsForUser(userId: string): Promise<{
|
|
33
|
+
total: number;
|
|
34
|
+
pending: number;
|
|
35
|
+
completed: number;
|
|
36
|
+
failed: number;
|
|
37
|
+
withEmbeddings: number;
|
|
38
|
+
}>;
|
|
39
|
+
}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.AssetMetadataRepository = void 0;
|
|
16
|
+
const typeorm_1 = require("typeorm");
|
|
17
|
+
const common_1 = require("@nestjs/common");
|
|
18
|
+
const typeorm_2 = require("@nestjs/typeorm");
|
|
19
|
+
const base_repository_1 = require("./base.repository");
|
|
20
|
+
const asset_metadata_entity_1 = require("../entities/asset-metadata.entity");
|
|
21
|
+
let AssetMetadataRepository = class AssetMetadataRepository extends base_repository_1.BaseRepository {
|
|
22
|
+
constructor(assetMetadataRepository) {
|
|
23
|
+
super(assetMetadataRepository);
|
|
24
|
+
this.assetMetadataRepository = assetMetadataRepository;
|
|
25
|
+
}
|
|
26
|
+
async findByAssetId(assetId) {
|
|
27
|
+
return this.assetMetadataRepository.findOne({
|
|
28
|
+
where: { assetId },
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
async findByUserId(userId) {
|
|
32
|
+
return this.assetMetadataRepository.find({
|
|
33
|
+
where: { userId },
|
|
34
|
+
order: { createdAt: 'DESC' },
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
async findByWorkspaceId(workspaceId) {
|
|
38
|
+
return this.assetMetadataRepository.find({
|
|
39
|
+
where: { workspaceId },
|
|
40
|
+
order: { createdAt: 'DESC' },
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
async findByStatus(status) {
|
|
44
|
+
return this.assetMetadataRepository.find({
|
|
45
|
+
where: { status },
|
|
46
|
+
order: { createdAt: 'ASC' },
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
async findPendingForProcessing(limit = 50) {
|
|
50
|
+
return this.assetMetadataRepository.find({
|
|
51
|
+
where: { status: asset_metadata_entity_1.AssetMetadataStatus.PENDING },
|
|
52
|
+
order: { createdAt: 'ASC' },
|
|
53
|
+
take: limit,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
async updateStatus(id, status, error) {
|
|
57
|
+
const updateData = { status };
|
|
58
|
+
if (error) {
|
|
59
|
+
updateData.processingError = error;
|
|
60
|
+
}
|
|
61
|
+
if (status === asset_metadata_entity_1.AssetMetadataStatus.COMPLETED) {
|
|
62
|
+
updateData.aiProcessedAt = new Date();
|
|
63
|
+
}
|
|
64
|
+
await this.assetMetadataRepository.update(id, updateData);
|
|
65
|
+
return this.findById(id);
|
|
66
|
+
}
|
|
67
|
+
async updateEmbedding(id, embedding, model, version) {
|
|
68
|
+
await this.assetMetadataRepository.update(id, {
|
|
69
|
+
contentEmbedding: embedding,
|
|
70
|
+
embeddingModel: model,
|
|
71
|
+
embeddingVersion: version,
|
|
72
|
+
embeddingGeneratedAt: new Date(),
|
|
73
|
+
});
|
|
74
|
+
return this.findById(id);
|
|
75
|
+
}
|
|
76
|
+
async updateContent(id, content) {
|
|
77
|
+
await this.assetMetadataRepository.update(id, {
|
|
78
|
+
content,
|
|
79
|
+
aiProcessedAt: new Date(),
|
|
80
|
+
});
|
|
81
|
+
return this.findById(id);
|
|
82
|
+
}
|
|
83
|
+
async updateTechnical(id, technical) {
|
|
84
|
+
await this.assetMetadataRepository.update(id, { technical });
|
|
85
|
+
return this.findById(id);
|
|
86
|
+
}
|
|
87
|
+
async findSimilarByEmbedding(embedding, userId, limit = 20, excludeAssetId, minSimilarity = 0.0) {
|
|
88
|
+
const embeddingStr = `[${embedding.join(',')}]`;
|
|
89
|
+
let query = this.assetMetadataRepository
|
|
90
|
+
.createQueryBuilder('am')
|
|
91
|
+
.select('am.*')
|
|
92
|
+
.addSelect(`1 - (COALESCE(am.content_embedding_vec, am.content_embedding::vector(1536)) <=> '${embeddingStr}'::vector(1536))`, 'similarity')
|
|
93
|
+
.where('am.user_id = :userId', { userId })
|
|
94
|
+
.andWhere('(am.content_embedding_vec IS NOT NULL OR am.content_embedding IS NOT NULL)');
|
|
95
|
+
if (excludeAssetId) {
|
|
96
|
+
query = query.andWhere('am.asset_id != :excludeAssetId', { excludeAssetId });
|
|
97
|
+
}
|
|
98
|
+
if (minSimilarity > 0) {
|
|
99
|
+
query = query.andWhere(`1 - (COALESCE(am.content_embedding_vec, am.content_embedding::vector(1536)) <=> '${embeddingStr}'::vector(1536)) >= :minSimilarity`, { minSimilarity });
|
|
100
|
+
}
|
|
101
|
+
const results = await query
|
|
102
|
+
.orderBy(`COALESCE(am.content_embedding_vec, am.content_embedding::vector(1536)) <=> '${embeddingStr}'::vector(1536)`, 'ASC')
|
|
103
|
+
.limit(limit)
|
|
104
|
+
.getRawMany();
|
|
105
|
+
return results;
|
|
106
|
+
}
|
|
107
|
+
async findSimilarByEmbeddingOptimized(embedding, userId, limit = 20, minSimilarity = 0.5) {
|
|
108
|
+
const embeddingStr = `[${embedding.join(',')}]`;
|
|
109
|
+
const results = await this.assetMetadataRepository.query(`SELECT * FROM search_similar_assets_optimized($1::vector(1536), $2, $3, $4)`, [embeddingStr, userId, limit, minSimilarity]);
|
|
110
|
+
return results.map((r) => ({
|
|
111
|
+
assetId: r.asset_id,
|
|
112
|
+
similarity: r.similarity,
|
|
113
|
+
category: r.category,
|
|
114
|
+
tags: r.tags,
|
|
115
|
+
description: r.description,
|
|
116
|
+
usageCount: r.usage_count,
|
|
117
|
+
lastUsedAt: r.last_used_at,
|
|
118
|
+
}));
|
|
119
|
+
}
|
|
120
|
+
async findByTags(userId, tags) {
|
|
121
|
+
return this.assetMetadataRepository
|
|
122
|
+
.createQueryBuilder('am')
|
|
123
|
+
.where('am.user_id = :userId', { userId })
|
|
124
|
+
.andWhere('am.tags && :tags', { tags })
|
|
125
|
+
.orderBy('am.created_at', 'DESC')
|
|
126
|
+
.getMany();
|
|
127
|
+
}
|
|
128
|
+
async searchByKeywords(userId, searchTerm) {
|
|
129
|
+
return this.assetMetadataRepository
|
|
130
|
+
.createQueryBuilder('am')
|
|
131
|
+
.where('am.user_id = :userId', { userId })
|
|
132
|
+
.andWhere(`(
|
|
133
|
+
am.description ILIKE :term
|
|
134
|
+
OR am.category ILIKE :term
|
|
135
|
+
OR :term = ANY(am.tags)
|
|
136
|
+
OR am.content->>'transcription' ILIKE :term
|
|
137
|
+
OR am.content->'keywords' ? :exactTerm
|
|
138
|
+
)`, { term: `%${searchTerm}%`, exactTerm: searchTerm })
|
|
139
|
+
.orderBy('am.created_at', 'DESC')
|
|
140
|
+
.getMany();
|
|
141
|
+
}
|
|
142
|
+
async incrementUsageCount(assetId, projectId) {
|
|
143
|
+
const metadata = await this.findByAssetId(assetId);
|
|
144
|
+
if (!metadata)
|
|
145
|
+
return;
|
|
146
|
+
const projectsUsedIn = metadata.projectsUsedIn || [];
|
|
147
|
+
if (projectId && !projectsUsedIn.includes(projectId)) {
|
|
148
|
+
projectsUsedIn.push(projectId);
|
|
149
|
+
}
|
|
150
|
+
await this.assetMetadataRepository
|
|
151
|
+
.createQueryBuilder()
|
|
152
|
+
.update()
|
|
153
|
+
.set({
|
|
154
|
+
usageCount: () => 'usage_count + 1',
|
|
155
|
+
lastUsedAt: new Date(),
|
|
156
|
+
projectsUsedIn,
|
|
157
|
+
})
|
|
158
|
+
.where('asset_id = :assetId', { assetId })
|
|
159
|
+
.execute();
|
|
160
|
+
}
|
|
161
|
+
async findWithoutEmbeddings(limit = 100) {
|
|
162
|
+
return this.assetMetadataRepository.find({
|
|
163
|
+
where: {
|
|
164
|
+
contentEmbedding: null,
|
|
165
|
+
status: asset_metadata_entity_1.AssetMetadataStatus.COMPLETED,
|
|
166
|
+
},
|
|
167
|
+
order: { createdAt: 'ASC' },
|
|
168
|
+
take: limit,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
async getStatsForUser(userId) {
|
|
172
|
+
const stats = await this.assetMetadataRepository
|
|
173
|
+
.createQueryBuilder('am')
|
|
174
|
+
.select('COUNT(*)', 'total')
|
|
175
|
+
.addSelect(`COUNT(*) FILTER (WHERE am.status = 'pending')`, 'pending')
|
|
176
|
+
.addSelect(`COUNT(*) FILTER (WHERE am.status = 'completed')`, 'completed')
|
|
177
|
+
.addSelect(`COUNT(*) FILTER (WHERE am.status = 'failed')`, 'failed')
|
|
178
|
+
.addSelect(`COUNT(*) FILTER (WHERE am.content_embedding IS NOT NULL)`, 'withEmbeddings')
|
|
179
|
+
.where('am.user_id = :userId', { userId })
|
|
180
|
+
.getRawOne();
|
|
181
|
+
return {
|
|
182
|
+
total: parseInt(stats.total, 10),
|
|
183
|
+
pending: parseInt(stats.pending, 10),
|
|
184
|
+
completed: parseInt(stats.completed, 10),
|
|
185
|
+
failed: parseInt(stats.failed, 10),
|
|
186
|
+
withEmbeddings: parseInt(stats.withEmbeddings, 10),
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
exports.AssetMetadataRepository = AssetMetadataRepository;
|
|
191
|
+
exports.AssetMetadataRepository = AssetMetadataRepository = __decorate([
|
|
192
|
+
(0, common_1.Injectable)(),
|
|
193
|
+
__param(0, (0, typeorm_2.InjectRepository)(asset_metadata_entity_1.AssetMetadata)),
|
|
194
|
+
__metadata("design:paramtypes", [typeorm_1.Repository])
|
|
195
|
+
], AssetMetadataRepository);
|
|
@@ -24,3 +24,8 @@ export { VideoTranscodeRepository } from './video-transcode.repository';
|
|
|
24
24
|
export { VideoRenderRepository } from './video-render.repository';
|
|
25
25
|
export { VideoFilmstripRepository } from './video-filmstrip.repository';
|
|
26
26
|
export { AudioWaveformRepository } from './audio-waveform.repository';
|
|
27
|
+
export { AssetMetadataRepository } from './asset-metadata.repository';
|
|
28
|
+
export { ProjectMetadataRepository } from './project-metadata.repository';
|
|
29
|
+
export { UserContextRepository } from './user-context.repository';
|
|
30
|
+
export { AiEmbeddingRepository } from './ai-embedding.repository';
|
|
31
|
+
export { MetadataActivityLogRepository } from './metadata-activity-log.repository';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.AudioWaveformRepository = exports.VideoFilmstripRepository = exports.VideoRenderRepository = exports.VideoTranscodeRepository = exports.EffectRepository = exports.AssetRepository = exports.AiUsageRepository = exports.ReferralEventRepository = exports.RewardRuleRepository = exports.CreditTransactionRepository = exports.StripeWebhookRepository = exports.PaymentTransactionRepository = exports.SubscriptionUsageRepository = exports.UserSubscriptionRepository = exports.PlanRepository = exports.SubscriptionPlanRepository = exports.CommentRepository = exports.EditorProjectRepository = exports.InviteRepository = exports.WorkspaceMemberRepository = exports.WorkspaceRepository = exports.AuthSessionRepository = exports.UserOnboardingRepository = exports.UserProfileRepository = exports.UserRepository = exports.BaseRepository = void 0;
|
|
3
|
+
exports.MetadataActivityLogRepository = exports.AiEmbeddingRepository = exports.UserContextRepository = exports.ProjectMetadataRepository = exports.AssetMetadataRepository = exports.AudioWaveformRepository = exports.VideoFilmstripRepository = exports.VideoRenderRepository = exports.VideoTranscodeRepository = exports.EffectRepository = exports.AssetRepository = exports.AiUsageRepository = exports.ReferralEventRepository = exports.RewardRuleRepository = exports.CreditTransactionRepository = exports.StripeWebhookRepository = exports.PaymentTransactionRepository = exports.SubscriptionUsageRepository = exports.UserSubscriptionRepository = exports.PlanRepository = exports.SubscriptionPlanRepository = exports.CommentRepository = exports.EditorProjectRepository = exports.InviteRepository = exports.WorkspaceMemberRepository = exports.WorkspaceRepository = exports.AuthSessionRepository = exports.UserOnboardingRepository = exports.UserProfileRepository = exports.UserRepository = exports.BaseRepository = void 0;
|
|
4
4
|
var base_repository_1 = require("./base.repository");
|
|
5
5
|
Object.defineProperty(exports, "BaseRepository", { enumerable: true, get: function () { return base_repository_1.BaseRepository; } });
|
|
6
6
|
var user_repository_1 = require("./user.repository");
|
|
@@ -53,3 +53,13 @@ var video_filmstrip_repository_1 = require("./video-filmstrip.repository");
|
|
|
53
53
|
Object.defineProperty(exports, "VideoFilmstripRepository", { enumerable: true, get: function () { return video_filmstrip_repository_1.VideoFilmstripRepository; } });
|
|
54
54
|
var audio_waveform_repository_1 = require("./audio-waveform.repository");
|
|
55
55
|
Object.defineProperty(exports, "AudioWaveformRepository", { enumerable: true, get: function () { return audio_waveform_repository_1.AudioWaveformRepository; } });
|
|
56
|
+
var asset_metadata_repository_1 = require("./asset-metadata.repository");
|
|
57
|
+
Object.defineProperty(exports, "AssetMetadataRepository", { enumerable: true, get: function () { return asset_metadata_repository_1.AssetMetadataRepository; } });
|
|
58
|
+
var project_metadata_repository_1 = require("./project-metadata.repository");
|
|
59
|
+
Object.defineProperty(exports, "ProjectMetadataRepository", { enumerable: true, get: function () { return project_metadata_repository_1.ProjectMetadataRepository; } });
|
|
60
|
+
var user_context_repository_1 = require("./user-context.repository");
|
|
61
|
+
Object.defineProperty(exports, "UserContextRepository", { enumerable: true, get: function () { return user_context_repository_1.UserContextRepository; } });
|
|
62
|
+
var ai_embedding_repository_1 = require("./ai-embedding.repository");
|
|
63
|
+
Object.defineProperty(exports, "AiEmbeddingRepository", { enumerable: true, get: function () { return ai_embedding_repository_1.AiEmbeddingRepository; } });
|
|
64
|
+
var metadata_activity_log_repository_1 = require("./metadata-activity-log.repository");
|
|
65
|
+
Object.defineProperty(exports, "MetadataActivityLogRepository", { enumerable: true, get: function () { return metadata_activity_log_repository_1.MetadataActivityLogRepository; } });
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Repository } from 'typeorm';
|
|
2
|
+
import { BaseRepository } from './base.repository';
|
|
3
|
+
import { MetadataActivityLog, MetadataActivityAction, MetadataActivitySource } from '../entities/metadata-activity-log.entity';
|
|
4
|
+
export declare class MetadataActivityLogRepository extends BaseRepository<MetadataActivityLog> {
|
|
5
|
+
private readonly activityLogRepository;
|
|
6
|
+
constructor(activityLogRepository: Repository<MetadataActivityLog>);
|
|
7
|
+
logActivity(data: {
|
|
8
|
+
userId: string;
|
|
9
|
+
workspaceId?: string;
|
|
10
|
+
sessionId?: string;
|
|
11
|
+
entityType: string;
|
|
12
|
+
entityId: string;
|
|
13
|
+
action: MetadataActivityAction;
|
|
14
|
+
source?: MetadataActivitySource;
|
|
15
|
+
metadataBefore?: Record<string, any>;
|
|
16
|
+
metadataAfter?: Record<string, any>;
|
|
17
|
+
details?: Record<string, any>;
|
|
18
|
+
projectId?: string;
|
|
19
|
+
durationMs?: number;
|
|
20
|
+
}): Promise<MetadataActivityLog>;
|
|
21
|
+
findByUserId(userId: string, limit?: number): Promise<MetadataActivityLog[]>;
|
|
22
|
+
findByEntity(entityType: string, entityId: string, limit?: number): Promise<MetadataActivityLog[]>;
|
|
23
|
+
findByProjectId(projectId: string, limit?: number): Promise<MetadataActivityLog[]>;
|
|
24
|
+
findByAction(action: MetadataActivityAction, userId?: string, limit?: number): Promise<MetadataActivityLog[]>;
|
|
25
|
+
findByDateRange(userId: string, startDate: Date, endDate: Date): Promise<MetadataActivityLog[]>;
|
|
26
|
+
findRecentEditActions(userId: string, limit?: number): Promise<MetadataActivityLog[]>;
|
|
27
|
+
findAiInteractions(userId: string, limit?: number): Promise<MetadataActivityLog[]>;
|
|
28
|
+
getActionCounts(userId: string, startDate?: Date): Promise<Record<string, number>>;
|
|
29
|
+
getEffectUsageFromLogs(userId: string, startDate?: Date): Promise<Record<string, number>>;
|
|
30
|
+
getTransitionUsageFromLogs(userId: string, startDate?: Date): Promise<Record<string, number>>;
|
|
31
|
+
getActivityHeatmap(userId: string, days?: number): Promise<{
|
|
32
|
+
hourOfDay: Record<number, number>;
|
|
33
|
+
dayOfWeek: Record<number, number>;
|
|
34
|
+
}>;
|
|
35
|
+
cleanupOldLogs(retentionDays?: number): Promise<number>;
|
|
36
|
+
getSessionSummary(sessionId: string): Promise<{
|
|
37
|
+
actions: number;
|
|
38
|
+
duration: number;
|
|
39
|
+
actionBreakdown: Record<string, number>;
|
|
40
|
+
}>;
|
|
41
|
+
}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.MetadataActivityLogRepository = void 0;
|
|
16
|
+
const typeorm_1 = require("typeorm");
|
|
17
|
+
const common_1 = require("@nestjs/common");
|
|
18
|
+
const typeorm_2 = require("@nestjs/typeorm");
|
|
19
|
+
const base_repository_1 = require("./base.repository");
|
|
20
|
+
const metadata_activity_log_entity_1 = require("../entities/metadata-activity-log.entity");
|
|
21
|
+
let MetadataActivityLogRepository = class MetadataActivityLogRepository extends base_repository_1.BaseRepository {
|
|
22
|
+
constructor(activityLogRepository) {
|
|
23
|
+
super(activityLogRepository);
|
|
24
|
+
this.activityLogRepository = activityLogRepository;
|
|
25
|
+
}
|
|
26
|
+
async logActivity(data) {
|
|
27
|
+
return this.create({
|
|
28
|
+
...data,
|
|
29
|
+
source: data.source || metadata_activity_log_entity_1.MetadataActivitySource.USER,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
async findByUserId(userId, limit = 100) {
|
|
33
|
+
return this.activityLogRepository.find({
|
|
34
|
+
where: { userId },
|
|
35
|
+
order: { createdAt: 'DESC' },
|
|
36
|
+
take: limit,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
async findByEntity(entityType, entityId, limit = 50) {
|
|
40
|
+
return this.activityLogRepository.find({
|
|
41
|
+
where: { entityType, entityId },
|
|
42
|
+
order: { createdAt: 'DESC' },
|
|
43
|
+
take: limit,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
async findByProjectId(projectId, limit = 100) {
|
|
47
|
+
return this.activityLogRepository.find({
|
|
48
|
+
where: { projectId },
|
|
49
|
+
order: { createdAt: 'DESC' },
|
|
50
|
+
take: limit,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
async findByAction(action, userId, limit = 100) {
|
|
54
|
+
const where = { action };
|
|
55
|
+
if (userId) {
|
|
56
|
+
where.userId = userId;
|
|
57
|
+
}
|
|
58
|
+
return this.activityLogRepository.find({
|
|
59
|
+
where,
|
|
60
|
+
order: { createdAt: 'DESC' },
|
|
61
|
+
take: limit,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
async findByDateRange(userId, startDate, endDate) {
|
|
65
|
+
return this.activityLogRepository.find({
|
|
66
|
+
where: {
|
|
67
|
+
userId,
|
|
68
|
+
createdAt: (0, typeorm_1.Between)(startDate, endDate),
|
|
69
|
+
},
|
|
70
|
+
order: { createdAt: 'ASC' },
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
async findRecentEditActions(userId, limit = 500) {
|
|
74
|
+
const editActions = [
|
|
75
|
+
metadata_activity_log_entity_1.MetadataActivityAction.EDIT_CLIP_ADDED,
|
|
76
|
+
metadata_activity_log_entity_1.MetadataActivityAction.EDIT_TRANSITION_ADDED,
|
|
77
|
+
metadata_activity_log_entity_1.MetadataActivityAction.EDIT_EFFECT_ADDED,
|
|
78
|
+
metadata_activity_log_entity_1.MetadataActivityAction.EDIT_TEXT_ADDED,
|
|
79
|
+
metadata_activity_log_entity_1.MetadataActivityAction.EDIT_AUDIO_ADDED,
|
|
80
|
+
metadata_activity_log_entity_1.MetadataActivityAction.EDIT_TRIM,
|
|
81
|
+
];
|
|
82
|
+
return this.activityLogRepository
|
|
83
|
+
.createQueryBuilder('al')
|
|
84
|
+
.where('al.user_id = :userId', { userId })
|
|
85
|
+
.andWhere('al.action IN (:...actions)', { actions: editActions })
|
|
86
|
+
.orderBy('al.created_at', 'DESC')
|
|
87
|
+
.limit(limit)
|
|
88
|
+
.getMany();
|
|
89
|
+
}
|
|
90
|
+
async findAiInteractions(userId, limit = 100) {
|
|
91
|
+
const aiActions = [
|
|
92
|
+
metadata_activity_log_entity_1.MetadataActivityAction.AI_CONTEXT_REQUESTED,
|
|
93
|
+
metadata_activity_log_entity_1.MetadataActivityAction.AI_SUGGESTION_ACCEPTED,
|
|
94
|
+
metadata_activity_log_entity_1.MetadataActivityAction.AI_SUGGESTION_REJECTED,
|
|
95
|
+
metadata_activity_log_entity_1.MetadataActivityAction.AI_CONTENT_GENERATED,
|
|
96
|
+
];
|
|
97
|
+
return this.activityLogRepository
|
|
98
|
+
.createQueryBuilder('al')
|
|
99
|
+
.where('al.user_id = :userId', { userId })
|
|
100
|
+
.andWhere('al.action IN (:...actions)', { actions: aiActions })
|
|
101
|
+
.orderBy('al.created_at', 'DESC')
|
|
102
|
+
.limit(limit)
|
|
103
|
+
.getMany();
|
|
104
|
+
}
|
|
105
|
+
async getActionCounts(userId, startDate) {
|
|
106
|
+
let query = this.activityLogRepository
|
|
107
|
+
.createQueryBuilder('al')
|
|
108
|
+
.select('al.action', 'action')
|
|
109
|
+
.addSelect('COUNT(*)', 'count')
|
|
110
|
+
.where('al.user_id = :userId', { userId })
|
|
111
|
+
.groupBy('al.action');
|
|
112
|
+
if (startDate) {
|
|
113
|
+
query = query.andWhere('al.created_at >= :startDate', { startDate });
|
|
114
|
+
}
|
|
115
|
+
const results = await query.getRawMany();
|
|
116
|
+
return results.reduce((acc, row) => {
|
|
117
|
+
acc[row.action] = parseInt(row.count, 10);
|
|
118
|
+
return acc;
|
|
119
|
+
}, {});
|
|
120
|
+
}
|
|
121
|
+
async getEffectUsageFromLogs(userId, startDate) {
|
|
122
|
+
let query = this.activityLogRepository
|
|
123
|
+
.createQueryBuilder('al')
|
|
124
|
+
.where('al.user_id = :userId', { userId })
|
|
125
|
+
.andWhere('al.action = :action', {
|
|
126
|
+
action: metadata_activity_log_entity_1.MetadataActivityAction.EDIT_EFFECT_ADDED,
|
|
127
|
+
});
|
|
128
|
+
if (startDate) {
|
|
129
|
+
query = query.andWhere('al.created_at >= :startDate', { startDate });
|
|
130
|
+
}
|
|
131
|
+
const logs = await query.getMany();
|
|
132
|
+
const effectCounts = {};
|
|
133
|
+
for (const log of logs) {
|
|
134
|
+
const effectName = log.details?.effectName || log.details?.effect || 'unknown';
|
|
135
|
+
effectCounts[effectName] = (effectCounts[effectName] || 0) + 1;
|
|
136
|
+
}
|
|
137
|
+
return effectCounts;
|
|
138
|
+
}
|
|
139
|
+
async getTransitionUsageFromLogs(userId, startDate) {
|
|
140
|
+
let query = this.activityLogRepository
|
|
141
|
+
.createQueryBuilder('al')
|
|
142
|
+
.where('al.user_id = :userId', { userId })
|
|
143
|
+
.andWhere('al.action = :action', {
|
|
144
|
+
action: metadata_activity_log_entity_1.MetadataActivityAction.EDIT_TRANSITION_ADDED,
|
|
145
|
+
});
|
|
146
|
+
if (startDate) {
|
|
147
|
+
query = query.andWhere('al.created_at >= :startDate', { startDate });
|
|
148
|
+
}
|
|
149
|
+
const logs = await query.getMany();
|
|
150
|
+
const transitionCounts = {};
|
|
151
|
+
for (const log of logs) {
|
|
152
|
+
const transitionName = log.details?.transitionName || log.details?.transition || 'unknown';
|
|
153
|
+
transitionCounts[transitionName] =
|
|
154
|
+
(transitionCounts[transitionName] || 0) + 1;
|
|
155
|
+
}
|
|
156
|
+
return transitionCounts;
|
|
157
|
+
}
|
|
158
|
+
async getActivityHeatmap(userId, days = 30) {
|
|
159
|
+
const startDate = new Date(Date.now() - days * 24 * 60 * 60 * 1000);
|
|
160
|
+
const logs = await this.activityLogRepository.find({
|
|
161
|
+
where: {
|
|
162
|
+
userId,
|
|
163
|
+
createdAt: (0, typeorm_1.MoreThan)(startDate),
|
|
164
|
+
},
|
|
165
|
+
select: ['createdAt'],
|
|
166
|
+
});
|
|
167
|
+
const hourOfDay = {};
|
|
168
|
+
const dayOfWeek = {};
|
|
169
|
+
for (const log of logs) {
|
|
170
|
+
const date = new Date(log.createdAt);
|
|
171
|
+
const hour = date.getHours();
|
|
172
|
+
const day = date.getDay();
|
|
173
|
+
hourOfDay[hour] = (hourOfDay[hour] || 0) + 1;
|
|
174
|
+
dayOfWeek[day] = (dayOfWeek[day] || 0) + 1;
|
|
175
|
+
}
|
|
176
|
+
return { hourOfDay, dayOfWeek };
|
|
177
|
+
}
|
|
178
|
+
async cleanupOldLogs(retentionDays = 90) {
|
|
179
|
+
const cutoffDate = new Date(Date.now() - retentionDays * 24 * 60 * 60 * 1000);
|
|
180
|
+
const result = await this.activityLogRepository.delete({
|
|
181
|
+
createdAt: (0, typeorm_1.LessThan)(cutoffDate),
|
|
182
|
+
});
|
|
183
|
+
return result.affected ?? 0;
|
|
184
|
+
}
|
|
185
|
+
async getSessionSummary(sessionId) {
|
|
186
|
+
const logs = await this.activityLogRepository.find({
|
|
187
|
+
where: { sessionId },
|
|
188
|
+
order: { createdAt: 'ASC' },
|
|
189
|
+
});
|
|
190
|
+
if (logs.length === 0) {
|
|
191
|
+
return { actions: 0, duration: 0, actionBreakdown: {} };
|
|
192
|
+
}
|
|
193
|
+
const actionBreakdown = {};
|
|
194
|
+
let totalDuration = 0;
|
|
195
|
+
for (const log of logs) {
|
|
196
|
+
actionBreakdown[log.action] = (actionBreakdown[log.action] || 0) + 1;
|
|
197
|
+
if (log.durationMs) {
|
|
198
|
+
totalDuration += log.durationMs;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
if (totalDuration === 0 && logs.length > 1) {
|
|
202
|
+
const firstLog = logs[0];
|
|
203
|
+
const lastLog = logs[logs.length - 1];
|
|
204
|
+
totalDuration =
|
|
205
|
+
new Date(lastLog.createdAt).getTime() -
|
|
206
|
+
new Date(firstLog.createdAt).getTime();
|
|
207
|
+
}
|
|
208
|
+
return {
|
|
209
|
+
actions: logs.length,
|
|
210
|
+
duration: totalDuration,
|
|
211
|
+
actionBreakdown,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
exports.MetadataActivityLogRepository = MetadataActivityLogRepository;
|
|
216
|
+
exports.MetadataActivityLogRepository = MetadataActivityLogRepository = __decorate([
|
|
217
|
+
(0, common_1.Injectable)(),
|
|
218
|
+
__param(0, (0, typeorm_2.InjectRepository)(metadata_activity_log_entity_1.MetadataActivityLog)),
|
|
219
|
+
__metadata("design:paramtypes", [typeorm_1.Repository])
|
|
220
|
+
], MetadataActivityLogRepository);
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Repository } from 'typeorm';
|
|
2
|
+
import { BaseRepository } from './base.repository';
|
|
3
|
+
import { ProjectMetadata } from '../entities/project-metadata.entity';
|
|
4
|
+
export declare class ProjectMetadataRepository extends BaseRepository<ProjectMetadata> {
|
|
5
|
+
private readonly projectMetadataRepository;
|
|
6
|
+
constructor(projectMetadataRepository: Repository<ProjectMetadata>);
|
|
7
|
+
findByProjectId(projectId: string): Promise<ProjectMetadata | null>;
|
|
8
|
+
findByUserId(userId: string): Promise<ProjectMetadata[]>;
|
|
9
|
+
findByWorkspaceId(workspaceId: string): Promise<ProjectMetadata[]>;
|
|
10
|
+
findByProjectType(userId: string, projectType: string): Promise<ProjectMetadata[]>;
|
|
11
|
+
updateComposition(projectId: string, composition: ProjectMetadata['composition']): Promise<ProjectMetadata | null>;
|
|
12
|
+
updateStyleFingerprint(projectId: string, styleFingerprint: ProjectMetadata['styleFingerprint']): Promise<ProjectMetadata | null>;
|
|
13
|
+
updateEmbedding(projectId: string, embedding: number[]): Promise<ProjectMetadata | null>;
|
|
14
|
+
recordEditSession(projectId: string, sessionDurationMs: number): Promise<void>;
|
|
15
|
+
addAssetUsed(projectId: string, assetId: string): Promise<void>;
|
|
16
|
+
incrementEffectUsage(projectId: string, effectName: string): Promise<void>;
|
|
17
|
+
addExportToHistory(projectId: string, exportData: {
|
|
18
|
+
format: string;
|
|
19
|
+
resolution: string;
|
|
20
|
+
fileSize?: number;
|
|
21
|
+
}): Promise<void>;
|
|
22
|
+
recordAiSuggestion(projectId: string, suggestionType: string, accepted: boolean): Promise<void>;
|
|
23
|
+
findSimilarByEmbedding(embedding: number[], userId: string, limit?: number, excludeProjectId?: string): Promise<Array<ProjectMetadata & {
|
|
24
|
+
similarity: number;
|
|
25
|
+
}>>;
|
|
26
|
+
getStylePreferencesForUser(userId: string): Promise<{
|
|
27
|
+
topTransitions: Record<string, number>;
|
|
28
|
+
topEffects: Record<string, number>;
|
|
29
|
+
avgClipDuration: number;
|
|
30
|
+
preferredPacing: string;
|
|
31
|
+
}>;
|
|
32
|
+
}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.ProjectMetadataRepository = void 0;
|
|
16
|
+
const typeorm_1 = require("typeorm");
|
|
17
|
+
const common_1 = require("@nestjs/common");
|
|
18
|
+
const typeorm_2 = require("@nestjs/typeorm");
|
|
19
|
+
const base_repository_1 = require("./base.repository");
|
|
20
|
+
const project_metadata_entity_1 = require("../entities/project-metadata.entity");
|
|
21
|
+
let ProjectMetadataRepository = class ProjectMetadataRepository extends base_repository_1.BaseRepository {
|
|
22
|
+
constructor(projectMetadataRepository) {
|
|
23
|
+
super(projectMetadataRepository);
|
|
24
|
+
this.projectMetadataRepository = projectMetadataRepository;
|
|
25
|
+
}
|
|
26
|
+
async findByProjectId(projectId) {
|
|
27
|
+
return this.projectMetadataRepository.findOne({
|
|
28
|
+
where: { projectId },
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
async findByUserId(userId) {
|
|
32
|
+
return this.projectMetadataRepository.find({
|
|
33
|
+
where: { userId },
|
|
34
|
+
order: { updatedAt: 'DESC' },
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
async findByWorkspaceId(workspaceId) {
|
|
38
|
+
return this.projectMetadataRepository.find({
|
|
39
|
+
where: { workspaceId },
|
|
40
|
+
order: { updatedAt: 'DESC' },
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
async findByProjectType(userId, projectType) {
|
|
44
|
+
return this.projectMetadataRepository.find({
|
|
45
|
+
where: { userId, projectType },
|
|
46
|
+
order: { updatedAt: 'DESC' },
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
async updateComposition(projectId, composition) {
|
|
50
|
+
await this.projectMetadataRepository.update({ projectId }, { composition });
|
|
51
|
+
return this.findByProjectId(projectId);
|
|
52
|
+
}
|
|
53
|
+
async updateStyleFingerprint(projectId, styleFingerprint) {
|
|
54
|
+
await this.projectMetadataRepository.update({ projectId }, { styleFingerprint });
|
|
55
|
+
return this.findByProjectId(projectId);
|
|
56
|
+
}
|
|
57
|
+
async updateEmbedding(projectId, embedding) {
|
|
58
|
+
await this.projectMetadataRepository.update({ projectId }, {
|
|
59
|
+
projectEmbedding: embedding,
|
|
60
|
+
embeddingGeneratedAt: new Date(),
|
|
61
|
+
});
|
|
62
|
+
return this.findByProjectId(projectId);
|
|
63
|
+
}
|
|
64
|
+
async recordEditSession(projectId, sessionDurationMs) {
|
|
65
|
+
await this.projectMetadataRepository
|
|
66
|
+
.createQueryBuilder()
|
|
67
|
+
.update()
|
|
68
|
+
.set({
|
|
69
|
+
editSessionsCount: () => 'edit_sessions_count + 1',
|
|
70
|
+
totalEditTimeMs: () => `total_edit_time_ms + ${sessionDurationMs}`,
|
|
71
|
+
})
|
|
72
|
+
.where('project_id = :projectId', { projectId })
|
|
73
|
+
.execute();
|
|
74
|
+
}
|
|
75
|
+
async addAssetUsed(projectId, assetId) {
|
|
76
|
+
const metadata = await this.findByProjectId(projectId);
|
|
77
|
+
if (!metadata)
|
|
78
|
+
return;
|
|
79
|
+
const assetsUsed = metadata.assetsUsed || [];
|
|
80
|
+
if (!assetsUsed.includes(assetId)) {
|
|
81
|
+
assetsUsed.push(assetId);
|
|
82
|
+
await this.projectMetadataRepository.update({ projectId }, { assetsUsed });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
async incrementEffectUsage(projectId, effectName) {
|
|
86
|
+
const metadata = await this.findByProjectId(projectId);
|
|
87
|
+
if (!metadata)
|
|
88
|
+
return;
|
|
89
|
+
const effectsUsed = metadata.effectsUsed || {};
|
|
90
|
+
effectsUsed[effectName] = (effectsUsed[effectName] || 0) + 1;
|
|
91
|
+
await this.projectMetadataRepository.update({ projectId }, { effectsUsed });
|
|
92
|
+
}
|
|
93
|
+
async addExportToHistory(projectId, exportData) {
|
|
94
|
+
const metadata = await this.findByProjectId(projectId);
|
|
95
|
+
if (!metadata)
|
|
96
|
+
return;
|
|
97
|
+
const exportHistory = metadata.exportHistory || [];
|
|
98
|
+
exportHistory.push({
|
|
99
|
+
...exportData,
|
|
100
|
+
exportedAt: new Date().toISOString(),
|
|
101
|
+
});
|
|
102
|
+
await this.projectMetadataRepository.update({ projectId }, { exportHistory });
|
|
103
|
+
}
|
|
104
|
+
async recordAiSuggestion(projectId, suggestionType, accepted) {
|
|
105
|
+
const metadata = await this.findByProjectId(projectId);
|
|
106
|
+
if (!metadata)
|
|
107
|
+
return;
|
|
108
|
+
const aiSuggestionsApplied = metadata.aiSuggestionsApplied || [];
|
|
109
|
+
aiSuggestionsApplied.push({
|
|
110
|
+
suggestionType,
|
|
111
|
+
appliedAt: new Date().toISOString(),
|
|
112
|
+
accepted,
|
|
113
|
+
});
|
|
114
|
+
await this.projectMetadataRepository.update({ projectId }, { aiSuggestionsApplied });
|
|
115
|
+
}
|
|
116
|
+
async findSimilarByEmbedding(embedding, userId, limit = 10, excludeProjectId) {
|
|
117
|
+
const embeddingStr = JSON.stringify(embedding);
|
|
118
|
+
let query = this.projectMetadataRepository
|
|
119
|
+
.createQueryBuilder('pm')
|
|
120
|
+
.select('pm.*')
|
|
121
|
+
.addSelect(`1 - (pm.project_embedding::vector(1536) <=> '${embeddingStr}'::vector(1536))`, 'similarity')
|
|
122
|
+
.where('pm.user_id = :userId', { userId })
|
|
123
|
+
.andWhere('pm.project_embedding IS NOT NULL');
|
|
124
|
+
if (excludeProjectId) {
|
|
125
|
+
query = query.andWhere('pm.project_id != :excludeProjectId', {
|
|
126
|
+
excludeProjectId,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
const results = await query
|
|
130
|
+
.orderBy('similarity', 'DESC')
|
|
131
|
+
.limit(limit)
|
|
132
|
+
.getRawMany();
|
|
133
|
+
return results;
|
|
134
|
+
}
|
|
135
|
+
async getStylePreferencesForUser(userId) {
|
|
136
|
+
const projects = await this.projectMetadataRepository.find({
|
|
137
|
+
where: { userId },
|
|
138
|
+
select: ['styleFingerprint', 'effectsUsed'],
|
|
139
|
+
});
|
|
140
|
+
const transitions = {};
|
|
141
|
+
const effects = {};
|
|
142
|
+
let totalClipDuration = 0;
|
|
143
|
+
let clipDurationCount = 0;
|
|
144
|
+
const pacingCounts = {};
|
|
145
|
+
for (const project of projects) {
|
|
146
|
+
if (project.styleFingerprint?.transitions) {
|
|
147
|
+
for (const t of project.styleFingerprint.transitions) {
|
|
148
|
+
transitions[t] = (transitions[t] || 0) + 1;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
if (project.effectsUsed) {
|
|
152
|
+
for (const [effect, count] of Object.entries(project.effectsUsed)) {
|
|
153
|
+
effects[effect] = (effects[effect] || 0) + count;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if (project.styleFingerprint?.avgClipDuration) {
|
|
157
|
+
totalClipDuration += project.styleFingerprint.avgClipDuration;
|
|
158
|
+
clipDurationCount++;
|
|
159
|
+
}
|
|
160
|
+
if (project.styleFingerprint?.pacing) {
|
|
161
|
+
pacingCounts[project.styleFingerprint.pacing] =
|
|
162
|
+
(pacingCounts[project.styleFingerprint.pacing] || 0) + 1;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
let preferredPacing = 'medium';
|
|
166
|
+
let maxPacingCount = 0;
|
|
167
|
+
for (const [pacing, count] of Object.entries(pacingCounts)) {
|
|
168
|
+
if (count > maxPacingCount) {
|
|
169
|
+
maxPacingCount = count;
|
|
170
|
+
preferredPacing = pacing;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return {
|
|
174
|
+
topTransitions: transitions,
|
|
175
|
+
topEffects: effects,
|
|
176
|
+
avgClipDuration: clipDurationCount > 0 ? totalClipDuration / clipDurationCount : 0,
|
|
177
|
+
preferredPacing,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
exports.ProjectMetadataRepository = ProjectMetadataRepository;
|
|
182
|
+
exports.ProjectMetadataRepository = ProjectMetadataRepository = __decorate([
|
|
183
|
+
(0, common_1.Injectable)(),
|
|
184
|
+
__param(0, (0, typeorm_2.InjectRepository)(project_metadata_entity_1.ProjectMetadata)),
|
|
185
|
+
__metadata("design:paramtypes", [typeorm_1.Repository])
|
|
186
|
+
], ProjectMetadataRepository);
|
|
@@ -37,6 +37,11 @@ const repositories = [
|
|
|
37
37
|
_1.VideoRenderRepository,
|
|
38
38
|
_1.VideoFilmstripRepository,
|
|
39
39
|
_1.AudioWaveformRepository,
|
|
40
|
+
_1.AssetMetadataRepository,
|
|
41
|
+
_1.ProjectMetadataRepository,
|
|
42
|
+
_1.UserContextRepository,
|
|
43
|
+
_1.AiEmbeddingRepository,
|
|
44
|
+
_1.MetadataActivityLogRepository,
|
|
40
45
|
];
|
|
41
46
|
const entities = [
|
|
42
47
|
entities_1.User,
|
|
@@ -64,6 +69,11 @@ const entities = [
|
|
|
64
69
|
entities_1.VideoRender,
|
|
65
70
|
entities_1.VideoFilmstrip,
|
|
66
71
|
entities_1.AudioWaveform,
|
|
72
|
+
entities_1.AssetMetadata,
|
|
73
|
+
entities_1.ProjectMetadata,
|
|
74
|
+
entities_1.UserContext,
|
|
75
|
+
entities_1.AiEmbedding,
|
|
76
|
+
entities_1.MetadataActivityLog,
|
|
67
77
|
];
|
|
68
78
|
let RepositoryModule = class RepositoryModule {
|
|
69
79
|
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Repository } from 'typeorm';
|
|
2
|
+
import { BaseRepository } from './base.repository';
|
|
3
|
+
import { UserContext } from '../entities/user-context.entity';
|
|
4
|
+
export declare class UserContextRepository extends BaseRepository<UserContext> {
|
|
5
|
+
private readonly userContextRepository;
|
|
6
|
+
constructor(userContextRepository: Repository<UserContext>);
|
|
7
|
+
findByUserId(userId: string): Promise<UserContext | null>;
|
|
8
|
+
getOrCreate(userId: string): Promise<UserContext>;
|
|
9
|
+
updateStylePreferences(userId: string, preferences: {
|
|
10
|
+
preferredStyles?: string[];
|
|
11
|
+
preferredGenres?: string[];
|
|
12
|
+
preferredTransitions?: string[];
|
|
13
|
+
preferredEffects?: string[];
|
|
14
|
+
preferredFonts?: string[];
|
|
15
|
+
}): Promise<UserContext | null>;
|
|
16
|
+
updateDefaultSettings(userId: string, settings: {
|
|
17
|
+
defaultAspectRatio?: string;
|
|
18
|
+
defaultResolution?: string;
|
|
19
|
+
defaultFps?: number;
|
|
20
|
+
}): Promise<UserContext | null>;
|
|
21
|
+
updateBrandAssets(userId: string, brandAssets: {
|
|
22
|
+
brandColors?: UserContext['brandColors'];
|
|
23
|
+
brandFonts?: UserContext['brandFonts'];
|
|
24
|
+
brandLogoAssetIds?: string[];
|
|
25
|
+
brandWatermarkAssetId?: string;
|
|
26
|
+
}): Promise<UserContext | null>;
|
|
27
|
+
updateBehavioralAnalytics(userId: string, analytics: {
|
|
28
|
+
avgProjectDurationMs?: number;
|
|
29
|
+
avgSessionLengthMs?: number;
|
|
30
|
+
mostUsedEffects?: Record<string, number>;
|
|
31
|
+
mostUsedTransitions?: Record<string, number>;
|
|
32
|
+
editingPatterns?: UserContext['editingPatterns'];
|
|
33
|
+
skillLevel?: UserContext['skillLevel'];
|
|
34
|
+
activityHeatmap?: UserContext['activityHeatmap'];
|
|
35
|
+
}): Promise<UserContext | null>;
|
|
36
|
+
incrementProjectCount(userId: string, completed?: boolean): Promise<void>;
|
|
37
|
+
addEditTime(userId: string, durationMs: number): Promise<void>;
|
|
38
|
+
recordAiInteraction(userId: string, accepted: boolean): Promise<void>;
|
|
39
|
+
addRecentPrompt(userId: string, prompt: string, successful: boolean): Promise<void>;
|
|
40
|
+
updateEmbedding(userId: string, embedding: number[]): Promise<UserContext | null>;
|
|
41
|
+
markAggregated(userId: string): Promise<void>;
|
|
42
|
+
findNeedingAggregation(limit?: number): Promise<UserContext[]>;
|
|
43
|
+
getAiContext(userId: string): Promise<{
|
|
44
|
+
preferences: {
|
|
45
|
+
styles: string[];
|
|
46
|
+
genres: string[];
|
|
47
|
+
transitions: string[];
|
|
48
|
+
effects: string[];
|
|
49
|
+
};
|
|
50
|
+
defaults: {
|
|
51
|
+
aspectRatio: string;
|
|
52
|
+
resolution: string;
|
|
53
|
+
fps: number;
|
|
54
|
+
};
|
|
55
|
+
analytics: {
|
|
56
|
+
totalProjects: number;
|
|
57
|
+
avgProjectDuration: number;
|
|
58
|
+
skillLevel: string;
|
|
59
|
+
preferredPacing: string;
|
|
60
|
+
};
|
|
61
|
+
brand: {
|
|
62
|
+
colors: UserContext['brandColors'];
|
|
63
|
+
fonts: UserContext['brandFonts'];
|
|
64
|
+
};
|
|
65
|
+
} | null>;
|
|
66
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.UserContextRepository = void 0;
|
|
16
|
+
const typeorm_1 = require("typeorm");
|
|
17
|
+
const common_1 = require("@nestjs/common");
|
|
18
|
+
const typeorm_2 = require("@nestjs/typeorm");
|
|
19
|
+
const base_repository_1 = require("./base.repository");
|
|
20
|
+
const user_context_entity_1 = require("../entities/user-context.entity");
|
|
21
|
+
let UserContextRepository = class UserContextRepository extends base_repository_1.BaseRepository {
|
|
22
|
+
constructor(userContextRepository) {
|
|
23
|
+
super(userContextRepository);
|
|
24
|
+
this.userContextRepository = userContextRepository;
|
|
25
|
+
}
|
|
26
|
+
async findByUserId(userId) {
|
|
27
|
+
return this.userContextRepository.findOne({
|
|
28
|
+
where: { userId },
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
async getOrCreate(userId) {
|
|
32
|
+
let context = await this.findByUserId(userId);
|
|
33
|
+
if (!context) {
|
|
34
|
+
context = await this.create({ userId });
|
|
35
|
+
}
|
|
36
|
+
return context;
|
|
37
|
+
}
|
|
38
|
+
async updateStylePreferences(userId, preferences) {
|
|
39
|
+
await this.userContextRepository.update({ userId }, preferences);
|
|
40
|
+
return this.findByUserId(userId);
|
|
41
|
+
}
|
|
42
|
+
async updateDefaultSettings(userId, settings) {
|
|
43
|
+
await this.userContextRepository.update({ userId }, settings);
|
|
44
|
+
return this.findByUserId(userId);
|
|
45
|
+
}
|
|
46
|
+
async updateBrandAssets(userId, brandAssets) {
|
|
47
|
+
await this.userContextRepository.update({ userId }, brandAssets);
|
|
48
|
+
return this.findByUserId(userId);
|
|
49
|
+
}
|
|
50
|
+
async updateBehavioralAnalytics(userId, analytics) {
|
|
51
|
+
await this.userContextRepository.update({ userId }, analytics);
|
|
52
|
+
return this.findByUserId(userId);
|
|
53
|
+
}
|
|
54
|
+
async incrementProjectCount(userId, completed = false) {
|
|
55
|
+
const updates = {
|
|
56
|
+
totalProjectsCount: () => 'total_projects_count + 1',
|
|
57
|
+
};
|
|
58
|
+
if (completed) {
|
|
59
|
+
updates.completedProjectsCount = () => 'completed_projects_count + 1';
|
|
60
|
+
}
|
|
61
|
+
await this.userContextRepository
|
|
62
|
+
.createQueryBuilder()
|
|
63
|
+
.update()
|
|
64
|
+
.set(updates)
|
|
65
|
+
.where('user_id = :userId', { userId })
|
|
66
|
+
.execute();
|
|
67
|
+
}
|
|
68
|
+
async addEditTime(userId, durationMs) {
|
|
69
|
+
await this.userContextRepository
|
|
70
|
+
.createQueryBuilder()
|
|
71
|
+
.update()
|
|
72
|
+
.set({
|
|
73
|
+
totalEditTimeMs: () => `total_edit_time_ms + ${durationMs}`,
|
|
74
|
+
})
|
|
75
|
+
.where('user_id = :userId', { userId })
|
|
76
|
+
.execute();
|
|
77
|
+
}
|
|
78
|
+
async recordAiInteraction(userId, accepted) {
|
|
79
|
+
const updates = {
|
|
80
|
+
aiPromptsCount: () => 'ai_prompts_count + 1',
|
|
81
|
+
};
|
|
82
|
+
if (accepted) {
|
|
83
|
+
updates.aiSuggestionsAccepted = () => 'ai_suggestions_accepted + 1';
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
updates.aiSuggestionsRejected = () => 'ai_suggestions_rejected + 1';
|
|
87
|
+
}
|
|
88
|
+
await this.userContextRepository
|
|
89
|
+
.createQueryBuilder()
|
|
90
|
+
.update()
|
|
91
|
+
.set(updates)
|
|
92
|
+
.where('user_id = :userId', { userId })
|
|
93
|
+
.execute();
|
|
94
|
+
}
|
|
95
|
+
async addRecentPrompt(userId, prompt, successful) {
|
|
96
|
+
const context = await this.findByUserId(userId);
|
|
97
|
+
if (!context)
|
|
98
|
+
return;
|
|
99
|
+
const recentPrompts = context.recentPrompts || [];
|
|
100
|
+
recentPrompts.unshift({
|
|
101
|
+
prompt,
|
|
102
|
+
timestamp: new Date().toISOString(),
|
|
103
|
+
successful,
|
|
104
|
+
});
|
|
105
|
+
const trimmedPrompts = recentPrompts.slice(0, 20);
|
|
106
|
+
await this.userContextRepository.update({ userId }, { recentPrompts: trimmedPrompts });
|
|
107
|
+
}
|
|
108
|
+
async updateEmbedding(userId, embedding) {
|
|
109
|
+
await this.userContextRepository.update({ userId }, {
|
|
110
|
+
userEmbedding: embedding,
|
|
111
|
+
embeddingGeneratedAt: new Date(),
|
|
112
|
+
});
|
|
113
|
+
return this.findByUserId(userId);
|
|
114
|
+
}
|
|
115
|
+
async markAggregated(userId) {
|
|
116
|
+
await this.userContextRepository.update({ userId }, { lastAggregatedAt: new Date() });
|
|
117
|
+
}
|
|
118
|
+
async findNeedingAggregation(limit = 100) {
|
|
119
|
+
const oneDayAgo = new Date(Date.now() - 24 * 60 * 60 * 1000);
|
|
120
|
+
return this.userContextRepository
|
|
121
|
+
.createQueryBuilder('uc')
|
|
122
|
+
.where('(uc.last_aggregated_at IS NULL OR uc.last_aggregated_at < :oneDayAgo)', { oneDayAgo })
|
|
123
|
+
.orderBy('uc.last_aggregated_at', 'ASC', 'NULLS FIRST')
|
|
124
|
+
.limit(limit)
|
|
125
|
+
.getMany();
|
|
126
|
+
}
|
|
127
|
+
async getAiContext(userId) {
|
|
128
|
+
const context = await this.findByUserId(userId);
|
|
129
|
+
if (!context)
|
|
130
|
+
return null;
|
|
131
|
+
return {
|
|
132
|
+
preferences: {
|
|
133
|
+
styles: context.preferredStyles || [],
|
|
134
|
+
genres: context.preferredGenres || [],
|
|
135
|
+
transitions: context.preferredTransitions || [],
|
|
136
|
+
effects: context.preferredEffects || [],
|
|
137
|
+
},
|
|
138
|
+
defaults: {
|
|
139
|
+
aspectRatio: context.defaultAspectRatio || '16:9',
|
|
140
|
+
resolution: context.defaultResolution || '1080p',
|
|
141
|
+
fps: context.defaultFps || 30,
|
|
142
|
+
},
|
|
143
|
+
analytics: {
|
|
144
|
+
totalProjects: context.totalProjectsCount || 0,
|
|
145
|
+
avgProjectDuration: context.avgProjectDurationMs || 0,
|
|
146
|
+
skillLevel: context.skillLevel || 'beginner',
|
|
147
|
+
preferredPacing: context.editingPatterns?.preferredPacing || 'medium',
|
|
148
|
+
},
|
|
149
|
+
brand: {
|
|
150
|
+
colors: context.brandColors,
|
|
151
|
+
fonts: context.brandFonts,
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
exports.UserContextRepository = UserContextRepository;
|
|
157
|
+
exports.UserContextRepository = UserContextRepository = __decorate([
|
|
158
|
+
(0, common_1.Injectable)(),
|
|
159
|
+
__param(0, (0, typeorm_2.InjectRepository)(user_context_entity_1.UserContext)),
|
|
160
|
+
__metadata("design:paramtypes", [typeorm_1.Repository])
|
|
161
|
+
], UserContextRepository);
|