[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6F7YYNTS7ABACWVJBAB4P9A":3,"skill-same:01M6F7YYNTS7ABACWVJBAB4P9A":79},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":23,"publishedAt":44,"linkOnly":45,"markdownContent":46,"markdownBody":47,"frontMatter":48,"refName":56,"commitSha":57,"skillDirectory":58,"forksCount":59,"repositoryHtmlUrl":60,"repositoryHomepageUrl":61,"ownerHtmlUrl":62,"ownerBlogUrl":6,"ownerDescription":63,"updatedAt":64,"install":65,"resources":78},"01M6F7YYNTS7ABACWVJBAB4P9A","canva-multi-env-setup",null,"Configure Canva Connect API across development, staging, and production environments.\nUse when setting up multi-environment deployments, managing OAuth credentials per environment,\nor implementing environment-specific Canva configurations.\nTrigger with phrases like \"canva environments\", \"canva staging\",\n\"canva dev prod\", \"canva environment setup\", \"canva config by env\".","ComeOnOliver\u002Fskillshub","ComeOnOliver","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F43215937?v=4","01M6E5JNFECJAAVFE0W7SMWWD2","Haoran Shu","01M6E5JNNDQS7AJZDYFQ8TPZCK",66,"MIT","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fjeremylongshore\u002Fclaude-code-plugins-plus-skills\u002Fcanva-multi-env-setup\u002FSKILL.md",[18],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",[24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43],"canva multi env setup","setup","multi","env setup","env","canva","write","io","across","up","edit","design","read","configure","Bash","api","credentials","code","connect","config","2026-11-10 10:12:30",false,"---\nname: canva-multi-env-setup\ndescription: |\n  Configure Canva Connect API across development, staging, and production environments.\n  Use when setting up multi-environment deployments, managing OAuth credentials per environment,\n  or implementing environment-specific Canva configurations.\n  Trigger with phrases like \"canva environments\", \"canva staging\",\n  \"canva dev prod\", \"canva environment setup\", \"canva config by env\".\nallowed-tools: Read, Write, Edit, Bash(aws:*), Bash(gcloud:*), Bash(vault:*)\nversion: 1.0.0\nlicense: MIT\nauthor: Jeremy Longshore \u003Cjeremy@intentsolutions.io>\ntags: [saas, design, canva]\ncompatible-with: claude-code\n---\n\n# Canva Multi-Environment Setup\n\n## Overview\n\nConfigure Canva Connect API integrations across development, staging, and production. Each environment needs separate OAuth integrations registered in the Canva developer portal with distinct redirect URIs.\n\n## Environment Strategy\n\n| Environment | Canva Integration | Redirect URI | Data |\n|-------------|------------------|--------------|------|\n| Development | `my-app-dev` | `http:\u002F\u002Flocalhost:3000\u002Fauth\u002Fcanva\u002Fcallback` | Test account |\n| Staging | `my-app-staging` | `https:\u002F\u002Fstaging.myapp.com\u002Fauth\u002Fcanva\u002Fcallback` | Staging account |\n| Production | `my-app-prod` | `https:\u002F\u002Fmyapp.com\u002Fauth\u002Fcanva\u002Fcallback` | Real users |\n\n**Important:** Register a separate Canva integration per environment. Each gets its own client ID and secret.\n\n## Configuration\n\n```typescript\n\u002F\u002F src\u002Fconfig\u002Fcanva.ts\ninterface CanvaEnvConfig {\n  clientId: string;\n  clientSecret: string;\n  redirectUri: string;\n  baseUrl: string;  \u002F\u002F Always api.canva.com — Canva has no sandbox API\n  scopes: string[];\n  debug: boolean;\n}\n\nconst configs: Record\u003Cstring, CanvaEnvConfig> = {\n  development: {\n    clientId: process.env.CANVA_CLIENT_ID!,\n    clientSecret: process.env.CANVA_CLIENT_SECRET!,\n    redirectUri: 'http:\u002F\u002Flocalhost:3000\u002Fauth\u002Fcanva\u002Fcallback',\n    baseUrl: 'https:\u002F\u002Fapi.canva.com\u002Frest\u002Fv1', \u002F\u002F No sandbox exists\n    scopes: ['design:content:write', 'design:content:read', 'design:meta:read', 'asset:write', 'asset:read'],\n    debug: true,\n  },\n  staging: {\n    clientId: process.env.CANVA_CLIENT_ID!,\n    clientSecret: process.env.CANVA_CLIENT_SECRET!,\n    redirectUri: process.env.CANVA_REDIRECT_URI!,\n    baseUrl: 'https:\u002F\u002Fapi.canva.com\u002Frest\u002Fv1',\n    scopes: ['design:content:write', 'design:content:read', 'design:meta:read', 'asset:write', 'asset:read'],\n    debug: false,\n  },\n  production: {\n    clientId: process.env.CANVA_CLIENT_ID!,\n    clientSecret: process.env.CANVA_CLIENT_SECRET!,\n    redirectUri: process.env.CANVA_REDIRECT_URI!,\n    baseUrl: 'https:\u002F\u002Fapi.canva.com\u002Frest\u002Fv1',\n    scopes: ['design:content:write', 'design:content:read', 'design:meta:read'],\n    debug: false,\n  },\n};\n\nexport function getCanvaConfig(): CanvaEnvConfig {\n  const env = process.env.NODE_ENV || 'development';\n  return configs[env] || configs.development;\n}\n```\n\n## Secret Management\n\n### Local Development\n\n```bash\n# .env.local (git-ignored)\nCANVA_CLIENT_ID=OCA_dev_xxxxxxxx\nCANVA_CLIENT_SECRET=dev_xxxxxxxx\n```\n\n### GitHub Actions \u002F CI\n\n```bash\n# Per-environment secrets\ngh secret set CANVA_CLIENT_ID --env staging --body \"OCA_staging_xxx\"\ngh secret set CANVA_CLIENT_SECRET --env staging --body \"staging_xxx\"\ngh secret set CANVA_CLIENT_ID --env production --body \"OCA_prod_xxx\"\ngh secret set CANVA_CLIENT_SECRET --env production --body \"prod_xxx\"\n```\n\n### Production — Cloud Secret Managers\n\n```bash\n# GCP Secret Manager\ngcloud secrets create canva-client-id-prod --data-file=-\ngcloud secrets create canva-client-secret-prod --data-file=-\n\n# AWS Secrets Manager\naws secretsmanager create-secret \\\n  --name canva\u002Fproduction\u002Fclient-id \\\n  --secret-string \"OCA_prod_xxx\"\n\n# HashiCorp Vault\nvault kv put secret\u002Fcanva\u002Fproduction \\\n  client_id=\"OCA_prod_xxx\" \\\n  client_secret=\"prod_xxx\"\n```\n\n## Environment Isolation Guards\n\n```typescript\n\u002F\u002F Prevent accidental cross-environment operations\nfunction assertEnvironment(expected: string): void {\n  const actual = process.env.NODE_ENV || 'development';\n  if (actual !== expected) {\n    throw new Error(`Expected ${expected} environment, got ${actual}`);\n  }\n}\n\n\u002F\u002F Guard destructive operations\nasync function deleteAllUserDesigns(userId: string, token: string) {\n  assertEnvironment('development'); \u002F\u002F Block in staging\u002Fproduction\n  \u002F\u002F ...\n}\n```\n\n## Token Storage per Environment\n\n```typescript\n\u002F\u002F Development: file-based for convenience\n\u002F\u002F Staging\u002FProduction: encrypted database\n\nfunction getTokenStore(): TokenStore {\n  const env = process.env.NODE_ENV || 'development';\n\n  if (env === 'development') {\n    return new FileTokenStore('.canva-tokens.json'); \u002F\u002F git-ignored\n  }\n\n  return new DatabaseTokenStore({\n    connectionString: process.env.DATABASE_URL!,\n    encryptionKey: process.env.TOKEN_ENCRYPTION_KEY!,\n  });\n}\n```\n\n## Canva-Specific Considerations\n\n1. **No sandbox API** — Canva has no separate sandbox environment. All environments hit `api.canva.com\u002Frest\u002Fv1`. Use separate Canva accounts for dev\u002Fstaging.\n2. **Separate integrations** — Each environment should be a distinct integration in the Canva developer portal to avoid redirect URI conflicts.\n3. **Scope differences** — Use broader scopes in dev for testing, minimal scopes in production.\n4. **Token isolation** — Never share tokens across environments. Refresh tokens are single-use.\n\n## Error Handling\n\n| Issue | Cause | Solution |\n|-------|-------|----------|\n| Wrong redirect URI | Environment mismatch | Use per-environment integration |\n| Missing secret | Not deployed to env | Add via secret manager |\n| Token cross-contamination | Shared token store | Isolate by environment prefix |\n| Production guard triggered | Wrong NODE_ENV | Set correct environment variable |\n\n## Resources\n\n- [Canva Creating Integrations](https:\u002F\u002Fwww.canva.dev\u002Fdocs\u002Fconnect\u002Fcreating-integrations\u002F)\n- [Canva Authentication](https:\u002F\u002Fwww.canva.dev\u002Fdocs\u002Fconnect\u002Fauthentication\u002F)\n- [12-Factor App Config](https:\u002F\u002F12factor.net\u002Fconfig)\n\n## Next Steps\n\nFor observability setup, see `canva-observability`.\n\n","# Canva Multi-Environment Setup\n\n## Overview\n\nConfigure Canva Connect API integrations across development, staging, and production. Each environment needs separate OAuth integrations registered in the Canva developer portal with distinct redirect URIs.\n\n## Environment Strategy\n\n| Environment | Canva Integration | Redirect URI | Data |\n|-------------|------------------|--------------|------|\n| Development | `my-app-dev` | `http:\u002F\u002Flocalhost:3000\u002Fauth\u002Fcanva\u002Fcallback` | Test account |\n| Staging | `my-app-staging` | `https:\u002F\u002Fstaging.myapp.com\u002Fauth\u002Fcanva\u002Fcallback` | Staging account |\n| Production | `my-app-prod` | `https:\u002F\u002Fmyapp.com\u002Fauth\u002Fcanva\u002Fcallback` | Real users |\n\n**Important:** Register a separate Canva integration per environment. Each gets its own client ID and secret.\n\n## Configuration\n\n```typescript\n\u002F\u002F src\u002Fconfig\u002Fcanva.ts\ninterface CanvaEnvConfig {\n  clientId: string;\n  clientSecret: string;\n  redirectUri: string;\n  baseUrl: string;  \u002F\u002F Always api.canva.com — Canva has no sandbox API\n  scopes: string[];\n  debug: boolean;\n}\n\nconst configs: Record\u003Cstring, CanvaEnvConfig> = {\n  development: {\n    clientId: process.env.CANVA_CLIENT_ID!,\n    clientSecret: process.env.CANVA_CLIENT_SECRET!,\n    redirectUri: 'http:\u002F\u002Flocalhost:3000\u002Fauth\u002Fcanva\u002Fcallback',\n    baseUrl: 'https:\u002F\u002Fapi.canva.com\u002Frest\u002Fv1', \u002F\u002F No sandbox exists\n    scopes: ['design:content:write', 'design:content:read', 'design:meta:read', 'asset:write', 'asset:read'],\n    debug: true,\n  },\n  staging: {\n    clientId: process.env.CANVA_CLIENT_ID!,\n    clientSecret: process.env.CANVA_CLIENT_SECRET!,\n    redirectUri: process.env.CANVA_REDIRECT_URI!,\n    baseUrl: 'https:\u002F\u002Fapi.canva.com\u002Frest\u002Fv1',\n    scopes: ['design:content:write', 'design:content:read', 'design:meta:read', 'asset:write', 'asset:read'],\n    debug: false,\n  },\n  production: {\n    clientId: process.env.CANVA_CLIENT_ID!,\n    clientSecret: process.env.CANVA_CLIENT_SECRET!,\n    redirectUri: process.env.CANVA_REDIRECT_URI!,\n    baseUrl: 'https:\u002F\u002Fapi.canva.com\u002Frest\u002Fv1',\n    scopes: ['design:content:write', 'design:content:read', 'design:meta:read'],\n    debug: false,\n  },\n};\n\nexport function getCanvaConfig(): CanvaEnvConfig {\n  const env = process.env.NODE_ENV || 'development';\n  return configs[env] || configs.development;\n}\n```\n\n## Secret Management\n\n### Local Development\n\n```bash\n# .env.local (git-ignored)\nCANVA_CLIENT_ID=OCA_dev_xxxxxxxx\nCANVA_CLIENT_SECRET=dev_xxxxxxxx\n```\n\n### GitHub Actions \u002F CI\n\n```bash\n# Per-environment secrets\ngh secret set CANVA_CLIENT_ID --env staging --body \"OCA_staging_xxx\"\ngh secret set CANVA_CLIENT_SECRET --env staging --body \"staging_xxx\"\ngh secret set CANVA_CLIENT_ID --env production --body \"OCA_prod_xxx\"\ngh secret set CANVA_CLIENT_SECRET --env production --body \"prod_xxx\"\n```\n\n### Production — Cloud Secret Managers\n\n```bash\n# GCP Secret Manager\ngcloud secrets create canva-client-id-prod --data-file=-\ngcloud secrets create canva-client-secret-prod --data-file=-\n\n# AWS Secrets Manager\naws secretsmanager create-secret \\\n  --name canva\u002Fproduction\u002Fclient-id \\\n  --secret-string \"OCA_prod_xxx\"\n\n# HashiCorp Vault\nvault kv put secret\u002Fcanva\u002Fproduction \\\n  client_id=\"OCA_prod_xxx\" \\\n  client_secret=\"prod_xxx\"\n```\n\n## Environment Isolation Guards\n\n```typescript\n\u002F\u002F Prevent accidental cross-environment operations\nfunction assertEnvironment(expected: string): void {\n  const actual = process.env.NODE_ENV || 'development';\n  if (actual !== expected) {\n    throw new Error(`Expected ${expected} environment, got ${actual}`);\n  }\n}\n\n\u002F\u002F Guard destructive operations\nasync function deleteAllUserDesigns(userId: string, token: string) {\n  assertEnvironment('development'); \u002F\u002F Block in staging\u002Fproduction\n  \u002F\u002F ...\n}\n```\n\n## Token Storage per Environment\n\n```typescript\n\u002F\u002F Development: file-based for convenience\n\u002F\u002F Staging\u002FProduction: encrypted database\n\nfunction getTokenStore(): TokenStore {\n  const env = process.env.NODE_ENV || 'development';\n\n  if (env === 'development') {\n    return new FileTokenStore('.canva-tokens.json'); \u002F\u002F git-ignored\n  }\n\n  return new DatabaseTokenStore({\n    connectionString: process.env.DATABASE_URL!,\n    encryptionKey: process.env.TOKEN_ENCRYPTION_KEY!,\n  });\n}\n```\n\n## Canva-Specific Considerations\n\n1. **No sandbox API** — Canva has no separate sandbox environment. All environments hit `api.canva.com\u002Frest\u002Fv1`. Use separate Canva accounts for dev\u002Fstaging.\n2. **Separate integrations** — Each environment should be a distinct integration in the Canva developer portal to avoid redirect URI conflicts.\n3. **Scope differences** — Use broader scopes in dev for testing, minimal scopes in production.\n4. **Token isolation** — Never share tokens across environments. Refresh tokens are single-use.\n\n## Error Handling\n\n| Issue | Cause | Solution |\n|-------|-------|----------|\n| Wrong redirect URI | Environment mismatch | Use per-environment integration |\n| Missing secret | Not deployed to env | Add via secret manager |\n| Token cross-contamination | Shared token store | Isolate by environment prefix |\n| Production guard triggered | Wrong NODE_ENV | Set correct environment variable |\n\n## Resources\n\n- [Canva Creating Integrations](https:\u002F\u002Fwww.canva.dev\u002Fdocs\u002Fconnect\u002Fcreating-integrations\u002F)\n- [Canva Authentication](https:\u002F\u002Fwww.canva.dev\u002Fdocs\u002Fconnect\u002Fauthentication\u002F)\n- [12-Factor App Config](https:\u002F\u002F12factor.net\u002Fconfig)\n\n## Next Steps\n\nFor observability setup, see `canva-observability`.\n\n",{"name":5,"tags":49,"author":51,"license":15,"version":52,"description":53,"allowed-tools":54,"compatible-with":55},[50,35,29],"saas","Jeremy Longshore \u003Cjeremy@intentsolutions.io>","1.0.0","Configure Canva Connect API across development, staging, and production environments.\nUse when setting up multi-environment deployments, managing OAuth credentials per environment,\nor implementing environment-specific Canva configurations.\nTrigger with phrases like \"canva environments\", \"canva staging\",\n\"canva dev prod\", \"canva environment setup\", \"canva config by env\".\n","Read, Write, Edit, Bash(aws:*), Bash(gcloud:*), Bash(vault:*)","claude-code","main","def8531e65114c0fca8fb8551c1871ee0eed705c","skills\u002Fjeremylongshore\u002Fclaude-code-plugins-plus-skills\u002Fcanva-multi-env-setup",24,"https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub","https:\u002F\u002Fskillshub.wtf","https:\u002F\u002Fgithub.com\u002FComeOnOliver","AI & Blockchain Engineer\r\n","2026-06-24 22:07:19",{"source":8,"skillName":5,"commands":66,"prompt":76,"zipAvailable":77},[67,70,73],{"tool":68,"command":69},"npx","npx skills add ComeOnOliver\u002Fskillshub --skill canva-multi-env-setup",{"tool":71,"command":72},"bunx","bunx skills add ComeOnOliver\u002Fskillshub --skill canva-multi-env-setup",{"tool":74,"command":75},"pnpm","pnpm dlx skills add ComeOnOliver\u002Fskillshub --skill canva-multi-env-setup","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fjeremylongshore\u002Fclaude-code-plugins-plus-skills\u002Fcanva-multi-env-setup\u002FSKILL.md",true,[],[80,94,104,113,119,126],{"skillNo":81,"name":82,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":83,"categories":84,"keywords":90,"publishedAt":44},"01M6FKJN13GMG4KY57J3QD8GY5","model-hierarchy-skill","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzscole\u002Fmodel-hierarchy-skill\u002Fmodel-hierarchy-skill\u002FSKILL.md",[85],{"categoryNo":86,"categoryName":87,"taxonomyType":88,"taxonomyTypeDesc":89},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",[91,92,93],"model","model hierarchy","model hierarchy skill",{"skillNo":95,"name":96,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":97,"categories":98,"keywords":100,"publishedAt":44},"01M6FKJJJ6GCAMR4PPPRD12M5X","zoom-mcp","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fzoom-mcp\u002FSKILL.md",[99],{"categoryNo":86,"categoryName":87,"taxonomyType":88,"taxonomyTypeDesc":89},[101,102,103],"MCP","zoom","zoom mcp",{"skillNo":105,"name":106,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":107,"categories":108,"keywords":109,"publishedAt":44},"01M6FKJE3KYT3KPEZMEEHYKA6K","zoom-apps-sdk","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fzoom-apps-sdk\u002FSKILL.md",[],[110,111,112,102],"sdk","zoom apps sdk","apps",{"skillNo":114,"name":115,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":116,"categories":117,"keywords":118,"publishedAt":44},"01M6FKJ9EDH9Q5VWDF8HAJRSP0","windows","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwindows\u002FSKILL.md",[],[115],{"skillNo":120,"name":121,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":122,"categories":123,"keywords":125,"publishedAt":44},"01M6FKJ666BEEFC3AE9GJ72Q93","whiteboard","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwhiteboard\u002FSKILL.md",[124],{"categoryNo":86,"categoryName":87,"taxonomyType":88,"taxonomyTypeDesc":89},[121],{"skillNo":127,"name":128,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":129,"categories":130,"keywords":131,"publishedAt":44},"01M6FKJ21WFCDGTJQ2Q91T9DVW","websockets","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwebsockets\u002FSKILL.md",[],[128]]