Refinament 1 #38
28
.agent/rules/svelte-rules.md
Normal file
28
.agent/rules/svelte-rules.md
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
---
|
||||||
|
trigger: glob
|
||||||
|
globs: **/*.svelte.ts,**/*.svelte
|
||||||
|
---
|
||||||
|
|
||||||
|
You are able to use the Svelte MCP server, where you have access to comprehensive Svelte 5 and SvelteKit documentation. Here's how to use the available tools effectively:
|
||||||
|
|
||||||
|
## Available MCP Tools:
|
||||||
|
|
||||||
|
### 1. list-sections
|
||||||
|
|
||||||
|
Use this FIRST to discover all available documentation sections. Returns a structured list with titles, use_cases, and paths.
|
||||||
|
When asked about Svelte or SvelteKit topics, ALWAYS use this tool at the start of the chat to find relevant sections.
|
||||||
|
|
||||||
|
### 2. get-documentation
|
||||||
|
|
||||||
|
Retrieves full documentation content for specific sections. Accepts single or multiple sections.
|
||||||
|
After calling the list-sections tool, you MUST analyze the returned documentation sections (especially the use_cases field) and then use the get-documentation tool to fetch ALL documentation sections that are relevant for the user's task.
|
||||||
|
|
||||||
|
### 3. svelte-autofixer
|
||||||
|
|
||||||
|
Analyzes Svelte code and returns issues and suggestions.
|
||||||
|
You MUST use this tool whenever writing Svelte code before sending it to the user. Keep calling it until no issues or suggestions are returned.
|
||||||
|
|
||||||
|
### 4. playground-link
|
||||||
|
|
||||||
|
Generates a Svelte Playground link with the provided code.
|
||||||
|
After completing the code, ask the user if they want a playground link. Only call this tool after user confirmation and NEVER if code was written to files in their project.
|
||||||
@@ -1,51 +1,45 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { useQuery, useConvexClient } from "convex-svelte";
|
import { useQuery, useConvexClient } from 'convex-svelte';
|
||||||
import { api } from "@sgse-app/backend/convex/_generated/api";
|
import { api } from '@sgse-app/backend/convex/_generated/api';
|
||||||
import type { Id } from "@sgse-app/backend/convex/_generated/dataModel";
|
import type { Id } from '@sgse-app/backend/convex/_generated/dataModel';
|
||||||
|
import type { FunctionReference } from 'convex/server';
|
||||||
|
|
||||||
const client = useConvexClient();
|
const client = useConvexClient();
|
||||||
const currentUser = useQuery(api.auth.getCurrentUser, {});
|
const currentUser = useQuery(api.auth.getCurrentUser as FunctionReference<'query'>);
|
||||||
const configAtual = useQuery(api.configuracaoEmail.obterConfigEmail, {});
|
const configAtual = useQuery(api.configuracaoEmail.obterConfigEmail);
|
||||||
|
|
||||||
let servidor = $state("");
|
let servidor = $state('');
|
||||||
let porta = $state(587);
|
let porta = $state(587);
|
||||||
let usuario = $state("");
|
let usuario = $state('');
|
||||||
let senha = $state("");
|
let senha = $state('');
|
||||||
let emailRemetente = $state("");
|
let emailRemetente = $state('');
|
||||||
let nomeRemetente = $state("");
|
let nomeRemetente = $state('');
|
||||||
let usarSSL = $state(false);
|
let usarSSL = $state(false);
|
||||||
let usarTLS = $state(true);
|
let usarTLS = $state(true);
|
||||||
let processando = $state(false);
|
let processando = $state(false);
|
||||||
let testando = $state(false);
|
let testando = $state(false);
|
||||||
let mensagem = $state<{ tipo: "success" | "error"; texto: string } | null>(
|
let mensagem = $state<{ tipo: 'success' | 'error'; texto: string } | null>(null);
|
||||||
null,
|
|
||||||
);
|
|
||||||
|
|
||||||
function mostrarMensagem(tipo: "success" | "error", texto: string) {
|
function mostrarMensagem(tipo: 'success' | 'error', texto: string) {
|
||||||
mensagem = { tipo, texto };
|
mensagem = { tipo, texto };
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
mensagem = null;
|
mensagem = null;
|
||||||
}, 5000);
|
}, 5000);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Carregar config existente
|
let dataLoaded = $state(false);
|
||||||
|
|
||||||
|
// Carregar config existente apenas uma vez
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if (configAtual?.data) {
|
if (configAtual?.data && !dataLoaded) {
|
||||||
servidor = configAtual.data.servidor || "";
|
servidor = configAtual.data.servidor || '';
|
||||||
porta = configAtual.data.porta || 587;
|
porta = configAtual.data.porta || 587;
|
||||||
usuario = configAtual.data.usuario || "";
|
usuario = configAtual.data.usuario || '';
|
||||||
emailRemetente = configAtual.data.emailRemetente || "";
|
emailRemetente = configAtual.data.emailRemetente || '';
|
||||||
nomeRemetente = configAtual.data.nomeRemetente || "";
|
nomeRemetente = configAtual.data.nomeRemetente || '';
|
||||||
usarSSL = configAtual.data.usarSSL || false;
|
usarSSL = configAtual.data.usarSSL || false;
|
||||||
usarTLS = configAtual.data.usarTLS || true;
|
usarTLS = configAtual.data.usarTLS || true;
|
||||||
}
|
dataLoaded = true;
|
||||||
});
|
|
||||||
|
|
||||||
// Tornar SSL e TLS mutuamente exclusivos
|
|
||||||
$effect(() => {
|
|
||||||
if (usarSSL && usarTLS) {
|
|
||||||
// Se ambos estão marcados, priorizar TLS por padrão
|
|
||||||
usarSSL = false;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -72,62 +66,61 @@
|
|||||||
!emailRemetente?.trim() ||
|
!emailRemetente?.trim() ||
|
||||||
!nomeRemetente?.trim()
|
!nomeRemetente?.trim()
|
||||||
) {
|
) {
|
||||||
mostrarMensagem("error", "Preencha todos os campos obrigatórios");
|
mostrarMensagem('error', 'Preencha todos os campos obrigatórios');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validação de porta (1-65535)
|
// Validação de porta (1-65535)
|
||||||
const portaNum = Number(porta);
|
const portaNum = Number(porta);
|
||||||
if (isNaN(portaNum) || portaNum < 1 || portaNum > 65535) {
|
if (isNaN(portaNum) || portaNum < 1 || portaNum > 65535) {
|
||||||
mostrarMensagem("error", "Porta deve ser um número entre 1 e 65535");
|
mostrarMensagem('error', 'Porta deve ser um número entre 1 e 65535');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validação de formato de email
|
// Validação de formato de email
|
||||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||||
if (!emailRegex.test(emailRemetente.trim())) {
|
if (!emailRegex.test(emailRemetente.trim())) {
|
||||||
mostrarMensagem("error", "Email remetente inválido");
|
mostrarMensagem('error', 'Email remetente inválido');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validação de senha: obrigatória apenas se não houver configuração existente
|
// Validação de senha: obrigatória apenas se não houver configuração existente
|
||||||
const temConfigExistente = configAtual?.data?.ativo;
|
const temConfigExistente = configAtual?.data?.ativo;
|
||||||
if (!temConfigExistente && !senha) {
|
if (!temConfigExistente && !senha) {
|
||||||
mostrarMensagem("error", "Senha é obrigatória para nova configuração");
|
mostrarMensagem('error', 'Senha é obrigatória para nova configuração');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!currentUser?.data) {
|
if (!currentUser?.data) {
|
||||||
mostrarMensagem("error", "Usuário não autenticado");
|
mostrarMensagem('error', 'Usuário não autenticado');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
processando = true;
|
processando = true;
|
||||||
try {
|
try {
|
||||||
const resultado = await client.mutation(
|
const resultado = await client.mutation(api.configuracaoEmail.salvarConfigEmail, {
|
||||||
api.configuracaoEmail.salvarConfigEmail,
|
|
||||||
{
|
|
||||||
servidor: servidor.trim(),
|
servidor: servidor.trim(),
|
||||||
porta: portaNum,
|
porta: portaNum,
|
||||||
usuario: usuario.trim(),
|
usuario: usuario.trim(),
|
||||||
senha: senha || "", // Senha vazia será tratada no backend
|
senha: senha || '', // Senha vazia será tratada no backend
|
||||||
emailRemetente: emailRemetente.trim(),
|
emailRemetente: emailRemetente.trim(),
|
||||||
nomeRemetente: nomeRemetente.trim(),
|
nomeRemetente: nomeRemetente.trim(),
|
||||||
usarSSL,
|
usarSSL,
|
||||||
usarTLS,
|
usarTLS,
|
||||||
configuradoPorId: currentUser.data._id as Id<"usuarios">,
|
configuradoPorId: currentUser.data._id as Id<'usuarios'>
|
||||||
},
|
});
|
||||||
);
|
|
||||||
|
|
||||||
if (resultado.sucesso) {
|
if (resultado.sucesso) {
|
||||||
mostrarMensagem("success", "Configuração salva com sucesso!");
|
mostrarMensagem('success', 'Configuração salva com sucesso!');
|
||||||
senha = ""; // Limpar senha
|
senha = ''; // Limpar senha
|
||||||
} else {
|
} else {
|
||||||
mostrarMensagem("error", resultado.erro);
|
mostrarMensagem('error', resultado.erro);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof Error) {
|
||||||
|
console.error('Erro ao salvar configuração:', error);
|
||||||
|
mostrarMensagem('error', error.message || 'Erro ao salvar configuração');
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
|
||||||
console.error("Erro ao salvar configuração:", error);
|
|
||||||
mostrarMensagem("error", error.message || "Erro ao salvar configuração");
|
|
||||||
} finally {
|
} finally {
|
||||||
processando = false;
|
processando = false;
|
||||||
}
|
}
|
||||||
@@ -135,66 +128,56 @@
|
|||||||
|
|
||||||
async function testarConexao() {
|
async function testarConexao() {
|
||||||
if (!servidor?.trim() || !porta || !usuario?.trim() || !senha) {
|
if (!servidor?.trim() || !porta || !usuario?.trim() || !senha) {
|
||||||
mostrarMensagem("error", "Preencha os dados de conexão antes de testar");
|
mostrarMensagem('error', 'Preencha os dados de conexão antes de testar');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validação de porta
|
// Validação de porta
|
||||||
const portaNum = Number(porta);
|
const portaNum = Number(porta);
|
||||||
if (isNaN(portaNum) || portaNum < 1 || portaNum > 65535) {
|
if (isNaN(portaNum) || portaNum < 1 || portaNum > 65535) {
|
||||||
mostrarMensagem("error", "Porta deve ser um número entre 1 e 65535");
|
mostrarMensagem('error', 'Porta deve ser um número entre 1 e 65535');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
testando = true;
|
testando = true;
|
||||||
try {
|
try {
|
||||||
const resultado = await client.action(
|
const resultado = await client.action(api.configuracaoEmail.testarConexaoSMTP, {
|
||||||
api.configuracaoEmail.testarConexaoSMTP,
|
|
||||||
{
|
|
||||||
servidor: servidor.trim(),
|
servidor: servidor.trim(),
|
||||||
porta: portaNum,
|
porta: portaNum,
|
||||||
usuario: usuario.trim(),
|
usuario: usuario.trim(),
|
||||||
senha: senha,
|
senha: senha,
|
||||||
usarSSL,
|
usarSSL,
|
||||||
usarTLS,
|
usarTLS
|
||||||
},
|
});
|
||||||
);
|
|
||||||
|
|
||||||
if (resultado.sucesso) {
|
if (resultado.sucesso) {
|
||||||
mostrarMensagem(
|
mostrarMensagem('success', 'Conexão testada com sucesso! Servidor SMTP está respondendo.');
|
||||||
"success",
|
|
||||||
"Conexão testada com sucesso! Servidor SMTP está respondendo.",
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
mostrarMensagem("error", `Erro ao testar conexão: ${resultado.erro}`);
|
mostrarMensagem('error', `Erro ao testar conexão: ${resultado.erro}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof Error) {
|
||||||
|
console.error('Erro ao testar conexão:', error);
|
||||||
|
mostrarMensagem('error', error.message || 'Erro ao conectar com o servidor SMTP');
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
|
||||||
console.error("Erro ao testar conexão:", error);
|
|
||||||
mostrarMensagem(
|
|
||||||
"error",
|
|
||||||
error.message || "Erro ao conectar com o servidor SMTP",
|
|
||||||
);
|
|
||||||
} finally {
|
} finally {
|
||||||
testando = false;
|
testando = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const statusConfig = $derived(
|
const statusConfig = $derived(configAtual?.data?.ativo ? 'Configurado' : 'Não configurado');
|
||||||
configAtual?.data?.ativo ? "Configurado" : "Não configurado",
|
|
||||||
);
|
|
||||||
|
|
||||||
const isLoading = $derived(configAtual === undefined);
|
const isLoading = $derived(configAtual === undefined);
|
||||||
const hasError = $derived(configAtual === null && !isLoading);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="container mx-auto px-4 py-6 max-w-4xl">
|
<div class="container mx-auto max-w-4xl px-4 py-6">
|
||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
<div class="flex items-center justify-between mb-6">
|
<div class="mb-6 flex items-center justify-between">
|
||||||
<div class="flex items-center gap-4">
|
<div class="flex items-center gap-4">
|
||||||
<div class="p-3 bg-secondary/10 rounded-xl">
|
<div class="bg-secondary/10 rounded-xl p-3">
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
class="h-8 w-8 text-secondary"
|
class="text-secondary h-8 w-8"
|
||||||
fill="none"
|
fill="none"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
@@ -208,9 +191,7 @@
|
|||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h1 class="text-3xl font-bold text-base-content">
|
<h1 class="text-base-content text-3xl font-bold">Configurações de Email (SMTP)</h1>
|
||||||
Configurações de Email (SMTP)
|
|
||||||
</h1>
|
|
||||||
<p class="text-base-content/60 mt-1">
|
<p class="text-base-content/60 mt-1">
|
||||||
Configurar servidor de email para envio de notificações
|
Configurar servidor de email para envio de notificações
|
||||||
</p>
|
</p>
|
||||||
@@ -222,16 +203,16 @@
|
|||||||
{#if mensagem}
|
{#if mensagem}
|
||||||
<div
|
<div
|
||||||
class="alert mb-6"
|
class="alert mb-6"
|
||||||
class:alert-success={mensagem.tipo === "success"}
|
class:alert-success={mensagem.tipo === 'success'}
|
||||||
class:alert-error={mensagem.tipo === "error"}
|
class:alert-error={mensagem.tipo === 'error'}
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
class="stroke-current shrink-0 h-6 w-6"
|
class="h-6 w-6 shrink-0 stroke-current"
|
||||||
fill="none"
|
fill="none"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
>
|
>
|
||||||
{#if mensagem.tipo === "success"}
|
{#if mensagem.tipo === 'success'}
|
||||||
<path
|
<path
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round"
|
stroke-linejoin="round"
|
||||||
@@ -261,16 +242,12 @@
|
|||||||
|
|
||||||
<!-- Status -->
|
<!-- Status -->
|
||||||
{#if !isLoading}
|
{#if !isLoading}
|
||||||
<div
|
<div class="alert {configAtual?.data?.ativo ? 'alert-success' : 'alert-warning'} mb-6">
|
||||||
class="alert {configAtual?.data?.ativo
|
|
||||||
? 'alert-success'
|
|
||||||
: 'alert-warning'} mb-6"
|
|
||||||
>
|
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
fill="none"
|
fill="none"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
class="stroke-current shrink-0 w-6 h-6"
|
class="h-6 w-6 shrink-0 stroke-current"
|
||||||
>
|
>
|
||||||
{#if configAtual?.data?.ativo}
|
{#if configAtual?.data?.ativo}
|
||||||
<path
|
<path
|
||||||
@@ -292,9 +269,7 @@
|
|||||||
<strong>Status:</strong>
|
<strong>Status:</strong>
|
||||||
{statusConfig}
|
{statusConfig}
|
||||||
{#if configAtual?.data?.testadoEm}
|
{#if configAtual?.data?.testadoEm}
|
||||||
- Última conexão testada em {new Date(
|
- Última conexão testada em {new Date(configAtual.data.testadoEm).toLocaleString('pt-BR')}
|
||||||
configAtual.data.testadoEm,
|
|
||||||
).toLocaleString("pt-BR")}
|
|
||||||
{/if}
|
{/if}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -306,7 +281,7 @@
|
|||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h2 class="card-title mb-4">Dados do Servidor SMTP</h2>
|
<h2 class="card-title mb-4">Dados do Servidor SMTP</h2>
|
||||||
|
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||||
<!-- Servidor -->
|
<!-- Servidor -->
|
||||||
<div class="form-control md:col-span-1">
|
<div class="form-control md:col-span-1">
|
||||||
<label class="label" for="smtp-servidor">
|
<label class="label" for="smtp-servidor">
|
||||||
@@ -320,9 +295,7 @@
|
|||||||
class="input input-bordered"
|
class="input input-bordered"
|
||||||
/>
|
/>
|
||||||
<div class="label">
|
<div class="label">
|
||||||
<span class="label-text-alt"
|
<span class="label-text-alt">Ex: smtp.gmail.com, smtp.office365.com</span>
|
||||||
>Ex: smtp.gmail.com, smtp.office365.com</span
|
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -339,8 +312,7 @@
|
|||||||
class="input input-bordered"
|
class="input input-bordered"
|
||||||
/>
|
/>
|
||||||
<div class="label">
|
<div class="label">
|
||||||
<span class="label-text-alt">Comum: 587 (TLS), 465 (SSL), 25</span
|
<span class="label-text-alt">Comum: 587 (TLS), 465 (SSL), 25</span>
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -412,7 +384,7 @@
|
|||||||
|
|
||||||
<!-- Opções de Segurança -->
|
<!-- Opções de Segurança -->
|
||||||
<div class="divider"></div>
|
<div class="divider"></div>
|
||||||
<h3 class="font-bold mb-2">Configurações de Segurança</h3>
|
<h3 class="mb-2 font-bold">Configurações de Segurança</h3>
|
||||||
|
|
||||||
<div class="flex flex-wrap gap-6">
|
<div class="flex flex-wrap gap-6">
|
||||||
<div class="form-control">
|
<div class="form-control">
|
||||||
@@ -441,7 +413,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Ações -->
|
<!-- Ações -->
|
||||||
<div class="card-actions justify-end mt-6 gap-3">
|
<div class="card-actions mt-6 justify-end gap-3">
|
||||||
<button
|
<button
|
||||||
class="btn btn-outline btn-info"
|
class="btn btn-outline btn-info"
|
||||||
onclick={testarConexao}
|
onclick={testarConexao}
|
||||||
@@ -499,12 +471,12 @@
|
|||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<!-- Exemplos Comuns -->
|
<!-- Exemplos Comuns -->
|
||||||
<div class="card bg-base-100 shadow-xl mt-6">
|
<div class="card bg-base-100 mt-6 shadow-xl">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h2 class="card-title mb-4">Exemplos de Configuração</h2>
|
<h2 class="card-title mb-4">Exemplos de Configuração</h2>
|
||||||
|
|
||||||
<div class="overflow-x-auto">
|
<div class="overflow-x-auto">
|
||||||
<table class="table table-sm">
|
<table class="table-sm table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Provedor</th>
|
<th>Provedor</th>
|
||||||
@@ -550,7 +522,7 @@
|
|||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
fill="none"
|
fill="none"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
class="stroke-current shrink-0 w-6 h-6"
|
class="h-6 w-6 shrink-0 stroke-current"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
@@ -561,13 +533,11 @@
|
|||||||
</svg>
|
</svg>
|
||||||
<div>
|
<div>
|
||||||
<p>
|
<p>
|
||||||
<strong>Dica de Segurança:</strong> Para Gmail e outros provedores, você
|
<strong>Dica de Segurança:</strong> Para Gmail e outros provedores, você pode precisar gerar
|
||||||
pode precisar gerar uma "senha de app" específica em vez de usar sua senha
|
uma "senha de app" específica em vez de usar sua senha principal.
|
||||||
principal.
|
|
||||||
</p>
|
</p>
|
||||||
<p class="text-sm mt-1">
|
<p class="mt-1 text-sm">
|
||||||
Gmail: Conta Google → Segurança → Verificação em duas etapas → Senhas de
|
Gmail: Conta Google → Segurança → Verificação em duas etapas → Senhas de app
|
||||||
app
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user