* fix: simply billing * fix usage graph * imporve billing more + supporter prompt on self-hosting * revert service change * revert query builder * fix: comments
205 lines
5.0 KiB
TypeScript
205 lines
5.0 KiB
TypeScript
import { z } from 'zod';
|
|
|
|
import { db } from '@openpanel/db';
|
|
import { importQueue } from '@openpanel/queue';
|
|
import { zCreateImport } from '@openpanel/validation';
|
|
|
|
import { getProjectAccess } from '../access';
|
|
import {
|
|
TRPCAccessError,
|
|
TRPCBadRequestError,
|
|
TRPCNotFoundError,
|
|
} from '../errors';
|
|
import { createTRPCRouter, protectedProcedure } from '../trpc';
|
|
|
|
export const importRouter = createTRPCRouter({
|
|
list: protectedProcedure
|
|
.input(z.object({ projectId: z.string() }))
|
|
.query(async ({ input, ctx }) => {
|
|
const access = await getProjectAccess({
|
|
projectId: input.projectId,
|
|
userId: ctx.session.userId,
|
|
});
|
|
|
|
if (!access) {
|
|
throw TRPCAccessError('You do not have access to this project');
|
|
}
|
|
|
|
return db.import.findMany({
|
|
where: {
|
|
projectId: input.projectId,
|
|
},
|
|
orderBy: {
|
|
createdAt: 'desc',
|
|
},
|
|
});
|
|
}),
|
|
|
|
get: protectedProcedure
|
|
.input(z.object({ id: z.string() }))
|
|
.query(async ({ input, ctx }) => {
|
|
const importRecord = await db.import.findUniqueOrThrow({
|
|
where: {
|
|
id: input.id,
|
|
},
|
|
include: {
|
|
project: true,
|
|
},
|
|
});
|
|
|
|
const access = await getProjectAccess({
|
|
projectId: importRecord.projectId,
|
|
userId: ctx.session.userId,
|
|
});
|
|
|
|
if (!access) {
|
|
throw TRPCAccessError('You do not have access to this import');
|
|
}
|
|
|
|
return importRecord;
|
|
}),
|
|
|
|
create: protectedProcedure
|
|
.input(zCreateImport)
|
|
.mutation(async ({ input, ctx }) => {
|
|
const access = await getProjectAccess({
|
|
projectId: input.projectId,
|
|
userId: ctx.session.userId,
|
|
});
|
|
|
|
if (!access || (typeof access !== 'boolean' && access.level === 'read')) {
|
|
throw TRPCAccessError(
|
|
'You do not have permission to create imports for this project',
|
|
);
|
|
}
|
|
|
|
const organization = await db.organization.findFirst({
|
|
where: {
|
|
projects: {
|
|
some: {
|
|
id: input.projectId,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
if (!organization) {
|
|
throw TRPCNotFoundError(
|
|
'Could not start import, organization not found',
|
|
);
|
|
}
|
|
|
|
if (!organization.isActive) {
|
|
throw TRPCBadRequestError(
|
|
'You cannot start an import without an active subscription!',
|
|
);
|
|
}
|
|
|
|
// Create import record
|
|
const importRecord = await db.import.create({
|
|
data: {
|
|
projectId: input.projectId,
|
|
config: input.config,
|
|
status: 'pending',
|
|
},
|
|
});
|
|
|
|
// Add job to queue
|
|
const job = await importQueue.add('import', {
|
|
type: 'import',
|
|
payload: {
|
|
importId: importRecord.id,
|
|
},
|
|
});
|
|
|
|
// Update import record with job ID
|
|
await db.import.update({
|
|
where: { id: importRecord.id },
|
|
data: { jobId: job.id },
|
|
});
|
|
|
|
return {
|
|
...importRecord,
|
|
jobId: job.id,
|
|
};
|
|
}),
|
|
|
|
delete: protectedProcedure
|
|
.input(z.object({ id: z.string() }))
|
|
.mutation(async ({ input, ctx }) => {
|
|
const importRecord = await db.import.findUniqueOrThrow({
|
|
where: {
|
|
id: input.id,
|
|
},
|
|
});
|
|
|
|
const access = await getProjectAccess({
|
|
projectId: importRecord.projectId,
|
|
userId: ctx.session.userId,
|
|
});
|
|
|
|
if (!access || (typeof access !== 'boolean' && access.level === 'read')) {
|
|
throw TRPCAccessError(
|
|
'You do not have permission to delete imports for this project',
|
|
);
|
|
}
|
|
|
|
if (importRecord.jobId) {
|
|
const job = await importQueue.getJob(importRecord.jobId);
|
|
if (job) {
|
|
await job.remove();
|
|
}
|
|
}
|
|
|
|
return db.import.delete({
|
|
where: {
|
|
id: input.id,
|
|
},
|
|
});
|
|
}),
|
|
|
|
retry: protectedProcedure
|
|
.input(z.object({ id: z.string() }))
|
|
.mutation(async ({ input, ctx }) => {
|
|
const importRecord = await db.import.findUniqueOrThrow({
|
|
where: {
|
|
id: input.id,
|
|
},
|
|
});
|
|
|
|
const access = await getProjectAccess({
|
|
projectId: importRecord.projectId,
|
|
userId: ctx.session.userId,
|
|
});
|
|
|
|
if (!access || (typeof access !== 'boolean' && access.level === 'read')) {
|
|
throw TRPCAccessError(
|
|
'You do not have permission to retry imports for this project',
|
|
);
|
|
}
|
|
|
|
// Only allow retry for failed imports
|
|
if (importRecord.status !== 'failed') {
|
|
throw new Error('Only failed imports can be retried');
|
|
}
|
|
|
|
// Add new job to queue
|
|
const job = await importQueue.add('import', {
|
|
type: 'import',
|
|
payload: {
|
|
importId: importRecord.id,
|
|
},
|
|
});
|
|
|
|
// Update import record
|
|
return db.import.update({
|
|
where: { id: importRecord.id },
|
|
data: {
|
|
jobId: job.id,
|
|
status: 'pending',
|
|
errorMessage: null,
|
|
},
|
|
});
|
|
}),
|
|
});
|