{"id":52030000,"url":"https://github.com/nicklima/strapi-blog-cms","last_synced_at":"2026-08-01T12:02:15.439Z","repository":{"id":37091362,"uuid":"462328188","full_name":"nicklima/strapi-blog-cms","owner":"nicklima","description":"Backend application of a Headless blog using Strapi as CMS","archived":false,"fork":false,"pushed_at":"2026-05-19T01:09:36.000Z","size":24383,"stargazers_count":1,"open_issues_count":2,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-05-19T01:31:04.580Z","etag":null,"topics":["headless","strapi"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nicklima.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2022-02-22T14:14:15.000Z","updated_at":"2026-05-19T01:09:32.000Z","dependencies_parsed_at":"2023-02-09T03:01:05.431Z","dependency_job_id":"5f69c300-0766-411b-8b15-c59c3f315e73","html_url":"https://github.com/nicklima/strapi-blog-cms","commit_stats":null,"previous_names":[],"tags_count":0,"template":true,"template_full_name":null,"purl":"pkg:github/nicklima/strapi-blog-cms","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nicklima%2Fstrapi-blog-cms","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nicklima%2Fstrapi-blog-cms/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nicklima%2Fstrapi-blog-cms/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nicklima%2Fstrapi-blog-cms/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nicklima","download_url":"https://codeload.github.com/nicklima/strapi-blog-cms/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nicklima%2Fstrapi-blog-cms/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":36155953,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-07-20T02:08:10.276Z","status":"online","status_checked_at":"2026-08-01T02:00:05.789Z","response_time":100,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["headless","strapi"],"created_at":"2026-08-01T12:02:14.066Z","updated_at":"2026-08-01T12:02:15.423Z","avatar_url":"https://github.com/nicklima.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Starter - Strapi CMS\n\nTemplate base para criação de um CMS utilizando Strapi com `Cloudinary` e `PostgreSQL`.\n\n\u003cimg src=\"strapi-admin.png\"/\u003e\n\n\u003cbr /\u003e\n\n## Cloudinary\n\nAs configurações do Provider do Cloudinary estão no arquivo `./config/plugins.js` e os dados sensíveis devem ser passados via variáveis de ambiente, como mostrado no `.env.example`\n\n```js\nmodule.exports = ({ env }) =\u003e {\n  if (env(\"NODE_ENV\") === \"production\") {\n    return {\n      upload: {\n        config: {\n          provider: \"cloudinary\",\n          providerOptions: {\n            cloud_name: env(\"CLOUDINARY_NAME\"),\n            api_key: env(\"CLOUDINARY_KEY\"),\n            api_secret: env(\"CLOUDINARY_SECRET\"),\n          },\n        },\n      },\n    };\n  }\n\n  return;\n};\n```\n\nPara que as thumbnails possam ser vistas no painel devemos alterar as configurações de segurança no arquivo `./config/middlewares.js` e incluir o domínio do GCS\n\n```js\nmodule.exports = [\n  \"strapi::errors\",\n  \"strapi::security\",\n  {\n    name: \"strapi::security\",\n    config: {\n      contentSecurityPolicy: {\n        useDefaults: true,\n        directives: {\n          \"connect-src\": [\"'self'\", \"https:\"],\n          \"img-src\": [\"'self'\", \"data:\", \"blob:\", \"res.cloudinary.com\"],\n          \"media-src\": [\"'self'\", \"data:\", \"blob:\", \"res.cloudinary.com\"],\n          upgradeInsecureRequests: null,\n        },\n      },\n    },\n  },\n  \"strapi::cors\",\n  \"strapi::poweredBy\",\n  \"strapi::logger\",\n  \"strapi::query\",\n  \"strapi::body\",\n  \"strapi::favicon\",\n  \"strapi::public\",\n];\n```\n\n## Database\n\nEsse starter está configurado com `PostgreSQL` para configuração em ambiente de Produção e `SQLite` para o ambiente de desenvolvimento local.\\\nQuando em ambiente local os dados ficam armazenados na pasta `.tmp` na raiz do projeto.\n\nPara os detalhes de conexão do banco de produção, definir as informações nas variáveis de ambiente como demonstrado no `.env.example`\n\n```js\nconst path = require(\"path\");\n\nmodule.exports = ({ env }) =\u003e {\n  if (env(\"NODE_ENV\") === \"production\") {\n    return {\n      connection: {\n        client: \"postgres\",\n        connection: {\n          host: env(\"DATABASE_HOST\", \"127.0.0.1\"),\n          port: env.int(\"DATABASE_PORT\", 5432),\n          database: env(\"DATABASE_NAME\", \"strapi\"),\n          user: env(\"DATABASE_USERNAME\", \"root\"),\n          password: env(\"DATABASE_PASSWORD\", \"root\"),\n          ssl: {\n            rejectUnauthorized: env.bool(\"DATABASE_SSL\", false), // For self-signed certificates\n          },\n        },\n      },\n    };\n  }\n\n  return {\n    connection: {\n      client: \"sqlite\",\n      connection: {\n        filename: path.join(\n          __dirname,\n          \"../\",\n          env(\"DATABASE_FILENAME\", \".tmp/data.db\")\n        ),\n      },\n      useNullAsDefault: true,\n    },\n  };\n};\n```\n\n## Inserção de dados na instalação do Strapi\n\nO Strapi permite que utilizemos uma função para gerar conteúdo de marcação (dummy content) que pode ser aproveitada para subirmos uma nova LP com seus dados de forma automática.\n\nPara isso devemos utilizar a função `bootstrap` do Strapi definida nos arquivos `./src/index.js` e `.src/bootstrap.js` como no exemplo abaixo que cria automaticamente os artigos, categorias e autores desse stater e preenche também os dados de SEO com um conteúdo padrão para uso nas `aplicações Frontend`\n\n`./src/index.js`\n\n```js\n\"use strict\";\n\nconst fs = require(\"fs\");\nconst mime = require(\"mime-types\");\nconst set = require(\"lodash.set\");\nconst { categories, writers, articles, global } = require(\"../data/data.json\");\n\nconst isFirstRun = async () =\u003e {\n  const pluginStore = strapi.store({\n    environment: strapi.config.environment,\n    type: \"type\",\n    name: \"setup\",\n  });\n  const initHasRun = await pluginStore.get({ key: \"initHasRun\" });\n  await pluginStore.set({ key: \"initHasRun\", value: true });\n  return !initHasRun;\n};\n\nconst setPublicPermissions = async (newPermissions) =\u003e {\n  // Find the ID of the public role\n  const publicRole = await strapi\n    .query(\"plugin::users-permissions.role\")\n    .findOne({\n      where: {\n        type: \"public\",\n      },\n    });\n\n  // Create the new permissions and link them to the public role\n  const allPermissionsToCreate = [];\n  Object.keys(newPermissions).map((controller) =\u003e {\n    const actions = newPermissions[controller];\n    const permissionsToCreate = actions.map((action) =\u003e {\n      return strapi.query(\"plugin::users-permissions.permission\").create({\n        data: {\n          action: `api::${controller}.${controller}.${action}`,\n          role: publicRole.id,\n        },\n      });\n    });\n    allPermissionsToCreate.push(...permissionsToCreate);\n  });\n  await Promise.all(allPermissionsToCreate);\n};\n\nconst getFileSizeInBytes = (filePath) =\u003e {\n  const stats = fs.statSync(filePath);\n  const fileSizeInBytes = stats[\"size\"];\n  return fileSizeInBytes;\n};\n\nconst getFileData = (fileName) =\u003e {\n  const filePath = `./data/uploads/${fileName}`;\n\n  // Parse the file metadata\n  const size = getFileSizeInBytes(filePath);\n  const ext = fileName.split(\".\").pop();\n  const mimeType = mime.lookup(ext);\n\n  return {\n    path: filePath,\n    name: fileName,\n    size,\n    type: mimeType,\n  };\n};\n\n// Create an entry and attach files if there are any\nconst createEntry = async ({ model, entry, files }) =\u003e {\n  try {\n    if (files) {\n      for (const [key, file] of Object.entries(files)) {\n        // Get file name without the extension\n        const [fileName] = file.name.split(\".\");\n        // Upload each individual file\n        const uploadedFile = await strapi\n          .plugin(\"upload\")\n          .service(\"upload\")\n          .upload({\n            files: file,\n            data: {\n              fileInfo: {\n                alternativeText: fileName,\n                caption: fileName,\n                name: fileName,\n              },\n            },\n          });\n\n        // Attach each file to its entry\n        set(entry, key, uploadedFile[0].id);\n      }\n    }\n\n    // Actually create the entry in Strapi\n    const createdEntry = await strapi.entityService.create(\n      `api::${model}.${model}`,\n      {\n        data: entry,\n      }\n    );\n  } catch (e) {\n    console.log(\"model\", entry, e);\n  }\n};\n\n// Import Data from ./data/data.json\n\nasync function importCategories() {\n  return Promise.all(\n    categories.map((category) =\u003e {\n      return createEntry({ model: \"category\", entry: category });\n    })\n  );\n}\n\nasync function importWriters() {\n  return Promise.all(\n    writers.map(async (writer) =\u003e {\n      const files = {\n        picture: getFileData(`${writer.email}.jpg`),\n      };\n      return createEntry({\n        model: \"writer\",\n        entry: writer,\n        files,\n      });\n    })\n  );\n}\n\nasync function importArticles() {\n  return Promise.all(\n    articles.map((article) =\u003e {\n      const files = {\n        image: getFileData(`${article.slug}.jpg`),\n      };\n\n      return createEntry({\n        model: \"article\",\n        entry: {\n          ...article,\n          // Make sure it's not a draft\n          publishedAt: Date.now(),\n        },\n        files,\n      });\n    })\n  );\n}\n\n// Create Globals SEO data\nconst importGlobal = async () =\u003e {\n  const files = {\n    favicon: getFileData(\"favicon.png\"),\n    \"defaultSeo.shareImage\": getFileData(\"default-image.png\"),\n  };\n  return createEntry({ model: \"global\", entry: global, files });\n};\n\n// Call Import data functions\nconst importSeedData = async () =\u003e {\n  // Allow read of application content types\n  await setPublicPermissions({\n    global: [\"find\"],\n    category: [\"find\", \"findOne\"],\n    writer: [\"find\", \"findOne\"],\n    article: [\"find\", \"findOne\"],\n  });\n\n  console.log(\"Bootstraping data...\");\n  // Create all entries\n  await importCategories();\n  await importWriters();\n  await importArticles();\n  await importGlobal();\n};\n\n// Run bootstrap functions\nmodule.exports = async () =\u003e {\n  const shouldImportSeedData = await isFirstRun();\n\n  if (shouldImportSeedData) {\n    console.log(\"First install, let's check if we have to create some data...\");\n    try {\n      console.log(\"Setting up the template...\");\n      await importSeedData();\n      console.log(\"Content imported!\");\n    } catch (error) {\n      console.log(\"Could not import seed data...\");\n      console.error(error);\n    }\n  }\n};\n```\n\nNesse exemplo de código estamos utilizando os dados contidos no JSON `data.json` dentro da pasta `./data`.\\\nAs imagens importadas dessa forma estão inseridas dentro da pasta `./data/uploads` e serão tratadas no código.\n\nNesse código de exemplo as imagens precisam ser nomeadas com o mesmo slug definido no JSON.\\\n\n`NOTA:`\n\n```\nEssa função de bootstrap só irá ser executada na primeira instalação do painel.\nEntão não precisamos nos preocupar com sobrescrita dos dados.\n```\n\n## Changelog\n\n### 1.0.0\n\n- Criação da estrutura base do strapi na versão `4.1.0`\n- Inclusão do modelo das variáveis de ambiente do projeto no `.env.example`\n- Criação dos Content-Types `Articles`, `Categories`, `Writers` e `Globals`/`SEO` que irá gerar os METADADOS de SEO para uso nas aplicações frontend\n- Inserção automática do conteúdo de `POSTS`, `CATEGORIAS`, `GLOBALS/SEO` e geração do `ADMIN USER` utilizando a função `bootstrap()` do Strapi no arquivo `./src/bootstrap.js`\n\n### 1.0.1\n\n- Ajuste na ordem das funções do `./src/bootstrap.js` e remoção da variável de ambiente `BOOTSTRAP_CONTENT`\n- Ajuste dos tamanhos e pesos das imagens em `./data/uploads`\n- Inclusão dos campos de redes sociais no bootstrap dos `writers`\n- Atualização do strapi para a versão `4.1.1`\n- Removida a opção de criar usuário automaticamente com a função `createAdminUser()` para evitar bug com os `roles`\n\n\u003cbr/\u003e\n\n---\n\n\u003cbr/\u003e\n\n### 🚀 Sobre o Strapi\n\nO Strapi vem com uma [Interface de linha de comando](https://docs.strapi.io/developer-docs/latest/developer-resources/cli/CLI.html) (CLI) completa, que permite que você gerencie seu projeto em segundos.\n\n#### `develop`\n\nInicie seu aplicativo Strapi com o **autoReload** ativado. [Saiba mais](https://docs.strapi.io/developer-docs/latest/developer-resources/cli/CLI.html#strapi-develop)\n\n```\nnpm run develop\n# or\nyarn develop\n```\n\n#### `start`\n\nInicie seu aplicativo Strapi com o **autoReload** desativado. [Saiba mais](https://docs.strapi.io/developer-docs/latest/developer-resources/cli/CLI.html#strapi-start)\n\n```\nnpm run start\n# or\nyarn start\n```\n\n#### `build`\n\nConstrua seu painel de administração. [Saiba mais](https://docs.strapi.io/developer-docs/latest/developer-resources/cli/CLI.html#strapi-build)\n\n```\nnpm run build\n# or\nyarn build\n```\n\n### ⚙️ Deploy\n\nO Strapi oferece muitas opções de implantação possíveis para o seu projeto. Encontre aquele que combina com você na [seção de implantação da documentação](https://docs.strapi.io/developer-docs/latest/setup-deployment-guides/deployment.html).\n\n### 📚 Documentação\n\n- [Centro de recursos](https://strapi.io/resource-center) - Centro de recursos do Strapi.\n- [Documentação do Strapi](https://docs.strapi.io) - Documentação oficial da Strapi.\n- [Tutoriais Strapi](https://strapi.io/tutorials) - Lista de tutoriais feitos pela equipe principal e pela comunidade.\n- [Blog do Strapi](https://docs.strapi.io) - Blog oficial do Strapi contendo artigos feitos pela equipe Strapi e pela comunidade.\n- [Changelog](https://strapi.io/changelog) - Saiba mais sobre as atualizações do produto Strapi, novos recursos e melhorias gerais.\n\nSinta-se à vontade para conferir o [repositório Strapi GitHub](https://github.com/strapi/strapi). Seus comentários e contribuições são bem-vindos!\n\n### ✨ Comunidade\n\n- [Discord](https://discord.strapi.io) - Venha conversar com a comunidade Strapi, incluindo a equipe principal.\n- [Fórum](https://forum.strapi.io/) - Lugar para discutir, fazer perguntas e encontrar respostas, mostrar seu projeto Strapi e obter feedback ou apenas conversar com outros membros da Comunidade.\n- [Awesome Strapi](https://github.com/strapi/awesome-strapi) - Uma lista com curadoria de coisas incríveis relacionadas ao Strapi.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnicklima%2Fstrapi-blog-cms","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnicklima%2Fstrapi-blog-cms","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnicklima%2Fstrapi-blog-cms/lists"}