Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ import type { Env } from '@packrat/api/types/env';
const typedEnv = env as unknown as Env;

/**
* TikTok Container class that runs the Node.js TikTok service.
* App Container class that runs the Node.js TikTok service.
* Extends Cloudflare's Container class for proper container lifecycle management.
*
* This container handles TikTok API integration which requires a full Node.js runtime
* for libraries that don't work in the Cloudflare Workers environment.
*/
export class TikTokContainer extends Container<Env> {
export class AppContainer extends Container<Env> {
// Port the container listens on - use CONTAINER_PORT env var or default to 8080
defaultPort = Number(typedEnv.CONTAINER_PORT) || 8080;
// Time before container sleeps due to inactivity (5 minutes for TikTok API calls)
Expand Down
2 changes: 1 addition & 1 deletion packages/api/src/containers/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export { TikTokContainer } from './TikTokContainer';
export { AppContainer } from './AppContainer';
6 changes: 3 additions & 3 deletions packages/api/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { MessageBatch } from '@cloudflare/workers-types';
import { sentry } from '@hono/sentry';
import { OpenAPIHono } from '@hono/zod-openapi';
import { TikTokContainer } from '@packrat/api/containers';
import { AppContainer } from '@packrat/api/containers';
import { routes } from '@packrat/api/routes';
import { processQueueBatch } from '@packrat/api/services/etl/queue';
import type { Env } from '@packrat/api/types/env';
Expand Down Expand Up @@ -74,8 +74,8 @@ app.get('/', (c) => {
return c.text('PackRat API is running!');
});

// Export the TikTokContainer class for Cloudflare Container binding
export { TikTokContainer };
// Export the AppContainer class for Cloudflare Container binding
export { AppContainer };

export default {
fetch: app.fetch,
Expand Down
4 changes: 2 additions & 2 deletions packages/api/src/routes/packTemplates/generateFromTikTok.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,10 @@ async function fetchTikTokPostData(
url: string,
): Promise<{ imageUrls: string[]; videoUrl?: string; caption?: string; contentId?: string }> {
try {
const { TIKTOK_CONTAINER } = getEnv(c);
const { APP_CONTAINER } = getEnv(c);

// Get the container instance using the binding
const container = getContainer(TIKTOK_CONTAINER as any);
const container = getContainer(APP_CONTAINER as any);
Copy link

Copilot AI Mar 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid casting APP_CONTAINER to any when calling getContainer. APP_CONTAINER is already typed in ValidatedEnv, so prefer passing it directly or casting to the specific expected container binding type (via unknown if needed) to preserve type safety.

Suggested change
const container = getContainer(APP_CONTAINER as any);
const container = getContainer(APP_CONTAINER);

Copilot uses AI. Check for mistakes.

// Make request to the container's /import endpoint
const response = await container.fetch(
Expand Down
14 changes: 7 additions & 7 deletions packages/api/src/utils/env-validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ export const apiEnvSchema = z.object({
ETL_QUEUE: z.unknown(),
LOGS_QUEUE: z.unknown(),
EMBEDDINGS_QUEUE: z.unknown(),
// TikTok Container binding (Durable Object)
TIKTOK_CONTAINER: z.unknown(),
// App container Durable Object binding (APP_CONTAINER)
APP_CONTAINER: z.unknown(),
});

// Relaxed schema for test environments
Expand All @@ -81,7 +81,7 @@ const testEnvSchema = apiEnvSchema.partial().extend({
ETL_QUEUE: z.unknown().optional(),
LOGS_QUEUE: z.unknown().optional(),
EMBEDDINGS_QUEUE: z.unknown().optional(),
TIKTOK_CONTAINER: z.unknown().optional(),
APP_CONTAINER: z.unknown().optional(),
});

// Infer the base type from Zod schema
Expand All @@ -98,7 +98,7 @@ export type ValidatedEnv = Omit<
| 'ETL_QUEUE'
| 'LOGS_QUEUE'
| 'EMBEDDINGS_QUEUE'
| 'TIKTOK_CONTAINER'
| 'APP_CONTAINER'
> & {
// Properly typed Cloudflare bindings
CF_VERSION_METADATA: WorkerVersionMetadata;
Expand All @@ -109,8 +109,8 @@ export type ValidatedEnv = Omit<
ETL_QUEUE: Queue;
LOGS_QUEUE: Queue;
EMBEDDINGS_QUEUE: Queue;
// TikTok Container Durable Object binding
TIKTOK_CONTAINER: DurableObjectNamespace;
// AppContainer Durable Object binding (APP_CONTAINER)
APP_CONTAINER: DurableObjectNamespace;
};

// Cache for validated environments per request
Expand Down Expand Up @@ -161,7 +161,7 @@ export function getEnv(c: Context): ValidatedEnv {
ETL_QUEUE: rawEnv.ETL_QUEUE || validated.data.ETL_QUEUE,
LOGS_QUEUE: rawEnv.LOGS_QUEUE || validated.data.LOGS_QUEUE,
EMBEDDINGS_QUEUE: rawEnv.EMBEDDINGS_QUEUE || validated.data.EMBEDDINGS_QUEUE,
TIKTOK_CONTAINER: rawEnv.TIKTOK_CONTAINER || validated.data.TIKTOK_CONTAINER,
APP_CONTAINER: rawEnv.APP_CONTAINER || validated.data.APP_CONTAINER,
} as ValidatedEnv;

// Cache the result
Expand Down
2 changes: 1 addition & 1 deletion packages/api/test/generate-from-tiktok.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const createMockContainerFetch = (contentId?: string) =>
// Store the mock to allow reconfiguring per test
let mockContainerFetch = createMockContainerFetch();

// Mock the @cloudflare/containers module (needs Container class for TikTokContainer)
// Mock the @cloudflare/containers module (needs Container class for AppContainer)
vi.mock('@cloudflare/containers', () => ({
Container: class MockContainer {},
getContainer: vi.fn(() => ({
Expand Down
37 changes: 23 additions & 14 deletions packages/api/wrangler.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -64,35 +64,36 @@
"ai": {
"binding": "AI"
},
// TikTok Container configuration - runs Node.js TikTok API service
// App Container configuration - runs Node.js TikTok API service
"containers": [
{
"class_name": "TikTokContainer",
"class_name": "AppContainer",
"image": "./Dockerfile",
"max_instances": 5
}
],
"durable_objects": {
"bindings": [
{
"class_name": "TikTokContainer",
"name": "TIKTOK_CONTAINER"
"class_name": "AppContainer",
"name": "APP_CONTAINER"
}
]
},
// Migrations for Durable Objects (including TikTok Container)
// Migrations for Durable Objects (including App Container)
"migrations": [
{
"new_sqlite_classes": ["TikTokContainer"],
"tag": "v1"
},
{
"tag": "v2",
"deleted_classes": ["TikTokContainer"]
},
{
"tag": "v3",
"new_sqlite_classes": ["TikTokContainer"]
"renamed_classes": [
{
"from": "TikTokContainer",
"to": "AppContainer"
}
]
}
],
"env": {
Expand Down Expand Up @@ -157,24 +158,32 @@
"containers": [
{
"name": "packrat-api-container-dev",
"class_name": "TikTokContainer",
"class_name": "AppContainer",
"image": "./Dockerfile",
"max_instances": 5
}
],
"durable_objects": {
"bindings": [
{
"class_name": "TikTokContainer",
"name": "TIKTOK_CONTAINER"
"class_name": "AppContainer",
"name": "APP_CONTAINER"
}
]
},
// Migrations for Durable Objects (including TikTok Container)
// Migrations for Durable Objects (including App Container)
"migrations": [
{
"new_sqlite_classes": ["TikTokContainer"],
"tag": "v1"
Comment thread
mikib0 marked this conversation as resolved.
},
{
"new_sqlite_classes": ["AppContainer"],
"tag": "v2"
},
{
"deleted_classes": ["TikTokContainer"],
"tag": "v3"
}
]
}
Expand Down
Loading