@mevdragon/vidfarm-devcli 0.1.0 → 0.2.0

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.
Files changed (63) hide show
  1. package/.env.example +11 -4
  2. package/PLATFORM_SPEC.md +142 -2
  3. package/README.md +165 -16
  4. package/SKILL.developer.md +577 -0
  5. package/dist/infra/cdk/bin/vidfarm-prod.js +59 -0
  6. package/dist/infra/cdk/lib/vidfarm-prod-stack.js +212 -0
  7. package/dist/src/account-pages.js +578 -0
  8. package/dist/src/app.js +887 -66
  9. package/dist/src/cli.js +284 -5
  10. package/dist/src/config.js +24 -4
  11. package/dist/src/db.js +427 -18
  12. package/dist/src/dev-app.js +59 -12
  13. package/dist/src/homepage.js +441 -0
  14. package/dist/src/index.js +12 -7
  15. package/dist/src/lib/crypto.js +14 -0
  16. package/dist/src/lib/template-dna.js +542 -0
  17. package/dist/src/lib/template-style-options.js +49 -0
  18. package/dist/src/registry.js +54 -7
  19. package/dist/src/runtime.js +3 -1
  20. package/dist/src/services/auth.js +69 -5
  21. package/dist/src/services/jobs.js +23 -4
  22. package/dist/src/services/providers.js +74 -12
  23. package/dist/src/services/storage.js +52 -18
  24. package/dist/src/services/template-certification.js +160 -0
  25. package/dist/src/services/template-loader.js +37 -0
  26. package/dist/src/services/template-sources.js +135 -0
  27. package/dist/src/worker.js +19 -7
  28. package/dist/templates/template_0000/src/lib/images.js +242 -0
  29. package/dist/templates/template_0000/src/remotion/Root.js +33 -0
  30. package/dist/templates/template_0000/src/sdk.js +3 -0
  31. package/dist/templates/template_0000/src/style-options.js +51 -0
  32. package/dist/templates/template_0000/src/template-dna.js +9 -0
  33. package/dist/templates/template_0000/src/template.js +1217 -0
  34. package/package.json +9 -1
  35. package/templates/template_0000/README.md +121 -0
  36. package/templates/template_0000/SKILL.md +193 -0
  37. package/templates/template_0000/assets/Abel-Regular.ttf +0 -0
  38. package/templates/template_0000/assets/DMSerifDisplay-Regular.ttf +0 -0
  39. package/templates/template_0000/assets/Montserrat[wght].ttf +0 -0
  40. package/templates/template_0000/assets/SourceCodePro[wght].ttf +0 -0
  41. package/templates/template_0000/assets/TikTokSans-SemiBold.ttf +0 -0
  42. package/templates/template_0000/assets/Yesteryear-Regular.ttf +0 -0
  43. package/templates/template_0000/composition.json +11 -0
  44. package/templates/template_0000/package-lock.json +5137 -0
  45. package/templates/template_0000/package.json +30 -0
  46. package/templates/template_0000/research/preview/.gitkeep +1 -0
  47. package/templates/template_0000/research/source_notes.md +7 -0
  48. package/templates/template_0000/scripts/create-site.mjs +27 -0
  49. package/templates/template_0000/scripts/render-cloud.mjs +72 -0
  50. package/templates/template_0000/src/lib/images.ts +284 -0
  51. package/templates/template_0000/src/remotion/Root.js +33 -0
  52. package/templates/template_0000/src/remotion/Root.tsx +75 -0
  53. package/templates/template_0000/src/remotion/index.tsx +4 -0
  54. package/templates/template_0000/src/sdk.ts +122 -0
  55. package/templates/template_0000/src/style-options.js +51 -0
  56. package/templates/template_0000/src/style-options.ts +60 -0
  57. package/templates/template_0000/src/template-dna.ts +15 -0
  58. package/templates/template_0000/src/template.ts +1747 -0
  59. package/templates/template_0000/template.config.json +26 -0
  60. package/templates/template_0000/tsconfig.json +19 -0
  61. package/dist/templates/template_0000/demo-template.js +0 -196
  62. package/dist/templates/template_0000/remotion/Root.js +0 -66
  63. /package/dist/templates/template_0000/{remotion → src/remotion}/index.js +0 -0
@@ -0,0 +1,212 @@
1
+ import path from "node:path";
2
+ import { CfnOutput, Duration, RemovalPolicy, Stack } from "aws-cdk-lib";
3
+ import * as acm from "aws-cdk-lib/aws-certificatemanager";
4
+ import * as ec2 from "aws-cdk-lib/aws-ec2";
5
+ import * as ecrAssets from "aws-cdk-lib/aws-ecr-assets";
6
+ import * as elbv2 from "aws-cdk-lib/aws-elasticloadbalancingv2";
7
+ import * as elbv2Targets from "aws-cdk-lib/aws-elasticloadbalancingv2-targets";
8
+ import * as iam from "aws-cdk-lib/aws-iam";
9
+ import * as route53 from "aws-cdk-lib/aws-route53";
10
+ import * as route53Targets from "aws-cdk-lib/aws-route53-targets";
11
+ import * as s3 from "aws-cdk-lib/aws-s3";
12
+ export class VidfarmProdStack extends Stack {
13
+ constructor(scope, id, props) {
14
+ super(scope, id, props);
15
+ const zone = route53.HostedZone.fromHostedZoneAttributes(this, "HostedZone", {
16
+ hostedZoneId: props.zoneId,
17
+ zoneName: props.zoneName
18
+ });
19
+ const domainName = `${props.recordName}.${props.zoneName}`;
20
+ const vpc = new ec2.Vpc(this, "VidfarmVpc", {
21
+ maxAzs: 2,
22
+ natGateways: 0,
23
+ subnetConfiguration: [
24
+ {
25
+ name: "public",
26
+ subnetType: ec2.SubnetType.PUBLIC
27
+ }
28
+ ]
29
+ });
30
+ const instanceSubnet = vpc.publicSubnets[0];
31
+ const certificate = new acm.Certificate(this, "VidfarmCertificate", {
32
+ domainName,
33
+ validation: acm.CertificateValidation.fromDns(zone)
34
+ });
35
+ const bucket = new s3.Bucket(this, "VidfarmBucket", {
36
+ encryption: s3.BucketEncryption.S3_MANAGED,
37
+ blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
38
+ versioned: true,
39
+ enforceSSL: true,
40
+ removalPolicy: RemovalPolicy.RETAIN,
41
+ autoDeleteObjects: false
42
+ });
43
+ const image = new ecrAssets.DockerImageAsset(this, "VidfarmImage", {
44
+ directory: path.resolve(process.cwd()),
45
+ platform: ecrAssets.Platform.LINUX_AMD64,
46
+ exclude: [
47
+ "cdk.out",
48
+ "dist",
49
+ "node_modules",
50
+ "data",
51
+ "data-*",
52
+ ".git",
53
+ ".claude",
54
+ ".claude/**",
55
+ ".vidfarm",
56
+ ".vidfarm/**",
57
+ "templates/*/node_modules",
58
+ "templates/*/dist",
59
+ "templates/*/.next",
60
+ "templates/*/.turbo"
61
+ ]
62
+ });
63
+ const role = new iam.Role(this, "VidfarmInstanceRole", {
64
+ assumedBy: new iam.ServicePrincipal("ec2.amazonaws.com")
65
+ });
66
+ role.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName("AmazonSSMManagedInstanceCore"));
67
+ role.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName("AmazonEC2ContainerRegistryReadOnly"));
68
+ bucket.grantReadWrite(role);
69
+ image.repository.grantPull(role);
70
+ const instanceSecurityGroup = new ec2.SecurityGroup(this, "VidfarmInstanceSecurityGroup", {
71
+ vpc,
72
+ allowAllOutbound: true
73
+ });
74
+ const albSecurityGroup = new ec2.SecurityGroup(this, "VidfarmAlbSecurityGroup", {
75
+ vpc,
76
+ allowAllOutbound: true
77
+ });
78
+ albSecurityGroup.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(80), "Allow HTTP");
79
+ albSecurityGroup.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(443), "Allow HTTPS");
80
+ albSecurityGroup.addIngressRule(ec2.Peer.anyIpv6(), ec2.Port.tcp(80), "Allow HTTP IPv6");
81
+ albSecurityGroup.addIngressRule(ec2.Peer.anyIpv6(), ec2.Port.tcp(443), "Allow HTTPS IPv6");
82
+ instanceSecurityGroup.addIngressRule(albSecurityGroup, ec2.Port.tcp(3000), "ALB to Vidfarm");
83
+ const instance = new ec2.Instance(this, "VidfarmInstance", {
84
+ vpc,
85
+ vpcSubnets: { subnets: [instanceSubnet] },
86
+ securityGroup: instanceSecurityGroup,
87
+ role,
88
+ machineImage: ec2.MachineImage.latestAmazonLinux2023(),
89
+ instanceType: new ec2.InstanceType(props.instanceType),
90
+ userDataCausesReplacement: true
91
+ });
92
+ const dataVolume = new ec2.CfnVolume(this, "VidfarmDataVolume", {
93
+ availabilityZone: instanceSubnet.availabilityZone,
94
+ encrypted: true,
95
+ size: props.dataVolumeSizeGiB,
96
+ volumeType: "gp3",
97
+ tags: [
98
+ {
99
+ key: "Name",
100
+ value: `${this.stackName}-data`
101
+ }
102
+ ]
103
+ });
104
+ dataVolume.applyRemovalPolicy(RemovalPolicy.RETAIN);
105
+ const volumeAttachment = new ec2.CfnVolumeAttachment(this, "VidfarmDataVolumeAttachment", {
106
+ device: "/dev/sdf",
107
+ instanceId: instance.instanceId,
108
+ volumeId: dataVolume.ref
109
+ });
110
+ instance.instance.addPropertyOverride("BlockDeviceMappings", [{
111
+ DeviceName: "/dev/xvda",
112
+ Ebs: {
113
+ DeleteOnTermination: false,
114
+ Encrypted: true,
115
+ VolumeSize: 30,
116
+ VolumeType: "gp3"
117
+ }
118
+ }]);
119
+ const envFile = [
120
+ "NODE_ENV=production",
121
+ "PORT=3000",
122
+ "VIDFARM_DATA_DIR=/app/data",
123
+ "VIDFARM_DB_PATH=/app/data/vidfarm.sqlite",
124
+ "TEMPLATE_SOURCE_ROOT=/app/data/template-sources",
125
+ `PUBLIC_BASE_URL=${props.publicBaseUrl}`,
126
+ `VIDFARM_ADMIN_EMAILS=${props.adminEmails}`,
127
+ `VIDFARM_DEVELOPER_EMAILS=${props.developerEmails}`,
128
+ "STORAGE_DRIVER=s3",
129
+ `AWS_REGION=${this.region}`,
130
+ `AWS_S3_BUCKET=${bucket.bucketName}`,
131
+ "AWS_S3_PUBLIC_READ=false",
132
+ `ENCRYPTION_SECRET=${props.encryptionSecret}`,
133
+ `API_KEY_SALT=${props.apiKeySalt}`,
134
+ `WEBHOOK_SECRET=${props.webhookSecret}`,
135
+ `RESEND_API_KEY=${props.resendApiKey ?? ""}`,
136
+ `RESEND_FROM_EMAIL=${props.resendFromEmail}`,
137
+ `SUPERAGENCY_KEY=${props.superagencyKey ?? ""}`,
138
+ `OPENAI_API_KEY=${props.openAiApiKey ?? ""}`,
139
+ `OPENROUTER_API_KEY=${props.openRouterApiKey ?? ""}`,
140
+ `GEMINI_API_KEY=${props.geminiApiKey ?? ""}`,
141
+ `PERPLEXITY_API_KEY=${props.perplexityApiKey ?? ""}`,
142
+ `REMOTION_MODE=${props.remotionMode}`,
143
+ `REMOTION_REGION=${props.remotionRegion}`,
144
+ `REMOTION_BUCKET_NAME=${props.remotionBucketName ?? ""}`,
145
+ `REMOTION_SITE_NAME=${props.remotionSiteName ?? ""}`,
146
+ `REMOTION_FUNCTION_NAME=${props.remotionFunctionName ?? ""}`,
147
+ `REMOTION_SERVE_URL=${props.remotionServeUrl ?? ""}`,
148
+ `REMOTION_COMPOSITION_ID=${props.remotionCompositionId}`,
149
+ `REMOTION_AWS_ACCESS_KEY_ID=${props.remotionAwsAccessKeyId ?? ""}`,
150
+ `REMOTION_AWS_SECRET_ACCESS_KEY=${props.remotionAwsSecretAccessKey ?? ""}`,
151
+ `MOCK_PROVIDER_RESPONSES=${props.mockProviderResponses}`,
152
+ `WORKER_POLL_MS=${props.workerPollMs}`,
153
+ `WORKER_BATCH_SIZE=${props.workerBatchSize}`,
154
+ `WORKER_MAX_CONCURRENT_JOBS=${props.workerMaxConcurrentJobs}`,
155
+ `DEFAULT_JOB_DELAY_SECONDS=${props.defaultJobDelaySeconds}`,
156
+ `MAX_PENDING_JOBS_GLOBAL=${props.maxPendingJobsGlobal}`,
157
+ `MAX_PENDING_JOBS_PER_CUSTOMER=${props.maxPendingJobsPerCustomer}`
158
+ ].join("\n");
159
+ instance.userData.addCommands("set -euxo pipefail", "dnf update -y", "dnf install -y docker awscli", "systemctl enable docker", "systemctl start docker", `VOL_ID="${dataVolume.ref}"`, "MOUNT_POINT=/var/lib/vidfarm", "mkdir -p ${MOUNT_POINT}", "DEVICE_PATH=/dev/xvdf", "if [ -e /dev/nvme1n1 ]; then DEVICE_PATH=/dev/nvme1n1; fi", "if ! blkid -L VIDFARM_DATA >/dev/null 2>&1; then mkfs -t ext4 ${DEVICE_PATH}; e2label ${DEVICE_PATH} VIDFARM_DATA; fi", "grep -q 'LABEL=VIDFARM_DATA /var/lib/vidfarm ext4 defaults,nofail 0 2' /etc/fstab || echo 'LABEL=VIDFARM_DATA /var/lib/vidfarm ext4 defaults,nofail 0 2' >> /etc/fstab", "mount -a", "mkdir -p /var/lib/vidfarm", "mkdir -p /opt/vidfarm", "cat > /opt/vidfarm/vidfarm.env <<'EOF'", envFile, "EOF", `aws ecr get-login-password --region ${this.region} | docker login --username AWS --password-stdin ${this.account}.dkr.ecr.${this.region}.amazonaws.com`, `docker pull ${image.imageUri}`, "cat > /etc/systemd/system/vidfarm.service <<'EOF'", "[Unit]", "Description=Vidfarm Docker Container", "After=docker.service network-online.target", "Requires=docker.service", "", "[Service]", "Restart=always", "RestartSec=10", `ExecStartPre=/usr/bin/docker pull ${image.imageUri}`, "ExecStartPre=-/usr/bin/docker rm -f vidfarm", `ExecStart=/usr/bin/docker run --name vidfarm --env-file /opt/vidfarm/vidfarm.env -p 3000:3000 -v /var/lib/vidfarm:/app/data ${image.imageUri}`, "ExecStop=/usr/bin/docker stop vidfarm", "", "[Install]", "WantedBy=multi-user.target", "EOF", "systemctl daemon-reload", "systemctl enable vidfarm", "systemctl restart vidfarm");
160
+ const alb = new elbv2.ApplicationLoadBalancer(this, "VidfarmAlb", {
161
+ vpc,
162
+ internetFacing: true,
163
+ securityGroup: albSecurityGroup
164
+ });
165
+ alb.addListener("HttpListener", {
166
+ port: 80,
167
+ open: true,
168
+ defaultAction: elbv2.ListenerAction.redirect({
169
+ protocol: "HTTPS",
170
+ port: "443",
171
+ permanent: true
172
+ })
173
+ });
174
+ const httpsListener = alb.addListener("HttpsListener", {
175
+ port: 443,
176
+ open: true,
177
+ certificates: [certificate]
178
+ });
179
+ httpsListener.addTargets("VidfarmInstanceTarget", {
180
+ protocol: elbv2.ApplicationProtocol.HTTP,
181
+ port: 3000,
182
+ targets: [new elbv2Targets.InstanceTarget(instance)],
183
+ healthCheck: {
184
+ path: "/health",
185
+ healthyHttpCodes: "200",
186
+ interval: Duration.seconds(30)
187
+ }
188
+ });
189
+ new route53.ARecord(this, "VidfarmAliasRecord", {
190
+ zone,
191
+ recordName: props.recordName,
192
+ target: route53.RecordTarget.fromAlias(new route53Targets.LoadBalancerTarget(alb))
193
+ });
194
+ new route53.AaaaRecord(this, "VidfarmAliasRecordIpv6", {
195
+ zone,
196
+ recordName: props.recordName,
197
+ target: route53.RecordTarget.fromAlias(new route53Targets.LoadBalancerTarget(alb))
198
+ });
199
+ new CfnOutput(this, "VidfarmUrl", {
200
+ value: `https://${domainName}`
201
+ });
202
+ new CfnOutput(this, "VidfarmBucketName", {
203
+ value: bucket.bucketName
204
+ });
205
+ new CfnOutput(this, "VidfarmInstanceId", {
206
+ value: instance.instanceId
207
+ });
208
+ new CfnOutput(this, "VidfarmDataVolumeId", {
209
+ value: dataVolume.ref
210
+ });
211
+ }
212
+ }