# Prompt mestre — microblog com GitHub Pages, Apps Script e Planilhas Copie todo o bloco abaixo e cole no Codex dentro do terminal do laboratório. O projeto é deliberadamente uma aplicação pública de demonstração: não use dados pessoais, notas, senhas ou informações sensíveis. ```text Você é meu par de programação e professor. Quero construir comigo um projeto didático completo, pequeno e sólido. Trabalhe dentro da pasta atual e não use frameworks, CDNs, bibliotecas externas, npm install ou serviços pagos. OBJETIVO Crie um microblog responsivo com identidade visual ORIGINAL, inspirado apenas nos padrões de usabilidade de feeds sociais modernos. Não copie nome, logotipo, ícones, textos, marca nem aparência exata do Twitter/X. O app deve permitir: - informar nome e @apelido; - publicar texto com no máximo 280 caracteres; - listar publicações mais recentes; - curtir uma publicação; - pesquisar no conteúdo já carregado; - mostrar estados de carregamento, sucesso, vazio e erro; - funcionar bem no celular e no desktop, com teclado e leitor de tela. Antes de escrever código, faça somente estas três perguntas: 1. Qual será o nome original do aplicativo? 2. Qual será o nome do repositório público no GitHub? 3. Qual cor de destaque eu prefiro? Depois apresente um plano curto e comece. Não peça outras decisões que você possa tomar com segurança. ARQUITETURA OBRIGATÓRIA Use HTML, CSS e JavaScript nativos, sem etapa de build. Organize assim: docs/ index.html styles.css config.js app.js core.mjs apps-script/ Code.gs appsscript.json tests/ core.test.mjs README.md .gitignore O GitHub Pages deve publicar diretamente a pasta /docs da branch main. Use somente caminhos relativos, porque o site ficará em /NOME-DO-REPOSITORIO/. Não coloque chaves, tokens, IDs privados ou senhas no repositório. A URL /exec do Apps Script não é segredo e ficará em docs/config.js. FRONT-END - Use HTML semântico, CSS moderno e JavaScript modular. - Crie uma marca, paleta e pequenos ícones SVG próprios. - Não use imagens ou fontes externas. - Faça layout mobile-first, foco visível, bom contraste e aria-live para mensagens importantes. - Nunca injete conteúdo do usuário com innerHTML; use textContent e criação de elementos DOM. - Valide nome, apelido e texto no cliente e novamente no servidor. - Desabilite o botão enquanto envia e impeça duplo clique. - Gere requestId com crypto.randomUUID() para toda alteração. - Separe funções puras em docs/core.mjs para que possam ser testadas com o módulo node:test, já disponível sem instalar pacotes. PADRÃO PARA APPS SCRIPT E CORS O front está no GitHub Pages e o backend é um Web App do Google Apps Script. Não finja que o ContentService fornece cabeçalhos CORS customizados: ele não é um backend CORS convencional e redireciona respostas para script.googleusercontent.com. Implemente este padrão didático: 1. LEITURAS POR JSONP - doGet(e) recebe action=list, action=find ou action=health. - Exija o parâmetro callback e valide-o estritamente com /^[A-Za-z_$][A-Za-z0-9_$]{0,63}$/. - Retorne callback(JSON_VALIDO) com MimeType.JAVASCRIPT. - No front, crie uma função JSONP que gere callback único, injete uma tag script, possua timeout, remova a tag e apague o callback ao terminar. - JSONP será usado somente para dados públicos e não sensíveis. 2. GRAVAÇÕES POR POST OPACO - Envie fetch para a URL /exec com method POST, mode "no-cors", Content-Type "text/plain;charset=utf-8" e corpo JSON. - Como a resposta é opaca, não tente ler status, headers ou JSON. - Depois do POST, consulte action=find&requestId=... por JSONP, com poucas tentativas e espera progressiva, para confirmar que a gravação aconteceu. - Se não houver confirmação, mostre mensagem honesta e botão para tentar novamente usando o MESMO requestId. 3. BACKEND ROBUSTO PARA A DEMONSTRAÇÃO - Use PropertiesService.getScriptProperties() para obter SPREADSHEET_ID. - Nunca grave o ID da planilha no repositório. - Crie uma função setup() que valida a propriedade e cria a aba Posts com cabeçalhos: id, requestId, createdAt, author, handle, text, likes. - Use LockService.getScriptLock() nas gravações. - Faça idempotência: se requestId já existir, não grave novamente. - Gere IDs no servidor com Utilities.getUuid(). - Use timestamp ISO criado no servidor. - Limite tamanhos, normalize espaços e rejeite campos desconhecidos. - Neutralize fórmulas de planilha em qualquer texto iniciado por =, +, - ou @ antes de setValues/appendRow, para evitar formula injection. - Use getDisplayValues() ao ler e converta likes para número com segurança. - Limite listagens a no máximo 30 itens e não devolva dados internos. - Para action=like, exija requestId próprio, localize o post por id, aplique idempotência e incremente dentro do lock. - Retorne erros públicos genéricos; não exponha stack trace, Spreadsheet ID, token ou detalhes da conta. LIMITAÇÃO DE SEGURANÇA QUE DEVE APARECER NO README E NA INTERFACE Este é um backend público e anônimo para conteúdo de aula. Qualquer pessoa que descobrir a URL pode tentar ler ou enviar dados. Não existe segredo seguro em um front estático. Não use este padrão com dados pessoais, autenticação real, notas, cadastros, pagamentos ou produção. Para produção, recomende um backend com autenticação, autorização, rate limiting e CORS controlado. TESTES E QUALIDADE - Crie testes com node:test para validação, normalização, limite de 280 caracteres, geração/aceitação de requestId e filtragem da pesquisa. - Execute: node --test tests/*.test.mjs - Faça uma revisão final procurando: segredos, innerHTML com dados, URLs absolutas de assets, dependências externas, falta de tratamento de timeout, duplicação por retry e fórmulas perigosas para a planilha. - Se algo não puder ser testado sem o deploy do Google, diga exatamente o que precisa ser testado manualmente; não invente resultado. README PASSO A PASSO Documente: 1. como criar uma Planilha Google vazia; 2. como copiar apps-script/Code.gs para um projeto Apps Script; 3. como adicionar SPREADSHEET_ID em Project Settings > Script Properties; 4. como executar setup() uma vez e autorizar o script; 5. como fazer Deploy > New deployment > Web app, executar como o proprietário e liberar "Anyone" somente para esta demonstração pública; 6. como copiar a URL terminada em /exec para docs/config.js; 7. como testar health, criar post, atualizar feed e curtir; 8. como publicar /docs no GitHub Pages; 9. como remover o deployment e excluir dados ao fim da atividade. Avise que contas Google Workspace gerenciadas podem não oferecer acesso anônimo, conforme a política do administrador. GITHUB — SOMENTE COM MINHA APROVAÇÃO Primeiro crie e teste tudo localmente. Depois mostre: - árvore dos arquivos; - resultado dos testes; - resumo da revisão de segurança; - comandos exatos que pretende executar no GitHub. Pare e espere eu aprovar antes de criar repositório, fazer push ou ativar GitHub Pages. Não execute login por mim, não peça token e nunca mostre ou salve token no projeto. Se gh auth status falhar, peça para eu executar pessoalmente: gh auth login --web --git-protocol https Depois da minha aprovação: - configure git user.name e user.email perguntando os valores, se estiverem ausentes; - inicialize Git com branch main; - faça um commit inicial claro; - crie o repositório público com: gh repo create NOME --public --source=. --remote=origin --push - descubra o owner com gh api user --jq .login; - ative Pages usando main e /docs pela API do GitHub; se a API negar a permissão, explique como fazer em Settings > Pages, sem tentar contornar; - aguarde o build e mostre as URLs do repositório e do site; - não apague, force-push, sobrescreva repositório existente nem altere outros projetos da conta. MODO DE TRABALHO Explique decisões importantes em português simples, mas não pare a cada arquivo. Faça mudanças em etapas, execute os testes e corrija os problemas. Preserve tudo dentro da pasta atual. No fim, entregue um checklist do que está pronto, do que eu ainda preciso fazer no navegador do Google e das limitações de segurança. ``` ## Observação para o professor Esse padrão evita depender de uma resposta CORS de `fetch`, mas não transforma Apps Script em um backend privado. A leitura por JSONP deve conter apenas dados públicos; a gravação é confirmada pelo `requestId`. Para aplicações reais, troque o backend por um serviço com autenticação e CORS configurável.