Añadir crear usuario a mariadb
This commit is contained in:
Generated
+13
@@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="DeviceTable">
|
||||||
|
<option name="columnSorters">
|
||||||
|
<list>
|
||||||
|
<ColumnSorterState>
|
||||||
|
<option name="column" value="Name" />
|
||||||
|
<option name="order" value="ASCENDING" />
|
||||||
|
</ColumnSorterState>
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
@@ -1,19 +1,21 @@
|
|||||||
# Saludapp
|
# Saludapp
|
||||||
|
|
||||||
Saludapp es una aplicación Android moderna enfocada en la salud y el bienestar, diseñada para ser multiusuario y conectada a una base de datos externa.
|
Saludapp es una aplicación Android moderna enfocada en la salud y el bienestar, diseñada para ser multiusuario y conectada a una base de datos externa MariaDB de forma segura.
|
||||||
|
|
||||||
## 🚀 Características
|
## 🚀 Características
|
||||||
- **Sistema Multiusuario:** Autenticación de usuarios mediante Login.
|
- **Sistema Multiusuario:** Registro e inicio de sesión de usuarios.
|
||||||
- **Conexión a Base de Datos:** Integración directa con **MariaDB** autohospedado.
|
- **Seguridad:** Encriptación de contraseñas mediante **SHA-256** para proteger los datos del usuario.
|
||||||
- **Interfaz Moderna:** Desarrollada íntegramente con **Jetpack Compose**.
|
- **Perfiles Completos:** Gestión de datos de usuario incluyendo nombre completo, nombre de usuario y email.
|
||||||
- **Diseño Material 3:** Uso de componentes y temas dinámicos de última generación.
|
- **Conexión a Base de Datos:** Integración directa con **MariaDB** autohospedado en `mariadb.peta9.com`.
|
||||||
- **Navegación Fluida:** Flujo desde pantalla de inicio hasta el panel principal.
|
- **Interfaz Moderna:** Desarrollada íntegramente con **Jetpack Compose** y **Material Design 3**.
|
||||||
|
- **Navegación Fluida:** Flujo completo desde bienvenida hasta registro, login y panel principal.
|
||||||
|
|
||||||
## 🛠️ Tecnologías utilizadas
|
## 🛠️ Tecnologías utilizadas
|
||||||
- **Lenguaje:** [Kotlin](https://kotlinlang.org/)
|
- **Lenguaje:** [Kotlin](https://kotlinlang.org/)
|
||||||
- **UI Framework:** [Jetpack Compose](https://developer.android.com/jetpack/compose)
|
- **UI Framework:** [Jetpack Compose](https://developer.android.com/jetpack/compose)
|
||||||
- **Base de Datos:** [MariaDB](https://mariadb.org/) (via MariaDB Java Client)
|
- **Base de Datos:** [MariaDB](https://mariadb.org/) (via MariaDB Java Client)
|
||||||
- **Asincronía:** Corrutinas de Kotlin para operaciones de red no bloqueantes.
|
- **Seguridad:** Algoritmo SHA-256 (MessageDigest) para hashing de credenciales.
|
||||||
|
- **Asincronía:** Corrutinas de Kotlin para operaciones de red eficientes.
|
||||||
- **Diseño:** [Material Design 3](https://m3.material.io/)
|
- **Diseño:** [Material Design 3](https://m3.material.io/)
|
||||||
|
|
||||||
## 📋 Requisitos previos
|
## 📋 Requisitos previos
|
||||||
@@ -27,20 +29,19 @@ Saludapp es una aplicación Android moderna enfocada en la salud y el bienestar,
|
|||||||
git clone https://github.com/tu-usuario/Saludapp.git
|
git clone https://github.com/tu-usuario/Saludapp.git
|
||||||
```
|
```
|
||||||
2. **Configurar Credenciales:**
|
2. **Configurar Credenciales:**
|
||||||
- Abre el archivo `app/src/main/java/com/example/saludapp/data/DatabaseHelper.kt`.
|
- La aplicación ya viene configurada para conectar con el servidor MariaDB.
|
||||||
- Sustituye `TU_CONTRASEÑA_AQUÍ` por la contraseña real de tu base de datos MariaDB.
|
- Las credenciales se encuentran en `app/src/main/java/com/example/saludapp/data/DatabaseHelper.kt`.
|
||||||
3. **Sincronizar Gradle:** Abre el proyecto en Android Studio y espera a que termine la sincronización de dependencias.
|
3. **Sincronizar Gradle:** Abre el proyecto en Android Studio y sincroniza las dependencias.
|
||||||
4. **Ejecutar:** Compila y ejecuta en un dispositivo o emulador con acceso a Internet.
|
4. **Ejecutar:** Compila y ejecuta la aplicación en un emulador o dispositivo real.
|
||||||
|
|
||||||
## 📂 Estructura del Proyecto
|
## 📂 Estructura del Proyecto
|
||||||
- `app/`: Módulo principal de la aplicación.
|
- `app/`: Módulo principal.
|
||||||
- `src/main/java/com/example/saludapp/`:
|
- `src/main/java/com/example/saludapp/data/DatabaseHelper.kt`: Gestión de seguridad (SHA-256) y conexión MariaDB.
|
||||||
- `MainActivity.kt`: Gestiona el flujo de navegación principal.
|
- `src/main/java/com/example/saludapp/ui/`:
|
||||||
- `data/DatabaseHelper.kt`: Lógica de conexión y consultas a MariaDB.
|
- `StartScreen.kt`: Pantalla de bienvenida.
|
||||||
- `ui/`: Contiene los componentes de la interfaz de usuario.
|
- `LoginScreen.kt`: Inicio de sesión con validación de credenciales encriptadas.
|
||||||
- `StartScreen.kt`: Pantalla de bienvenida.
|
- `RegisterScreen.kt`: Registro de nuevos usuarios con soporte para nombre completo.
|
||||||
- `LoginScreen.kt`: Interfaz de inicio de sesión con validación real.
|
- `theme/`: Configuración estética de Material 3.
|
||||||
- `theme/`: Definición de estilos y colores de Material 3.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
Desarrollado con ❤️ para mejorar la salud de los usuarios.
|
Desarrollado con ❤️ para mejorar la salud de los usuarios.
|
||||||
|
|||||||
@@ -31,6 +31,11 @@ android {
|
|||||||
buildFeatures {
|
buildFeatures {
|
||||||
compose = true
|
compose = true
|
||||||
}
|
}
|
||||||
|
packaging {
|
||||||
|
resources {
|
||||||
|
excludes += "/META-INF/{AL2.0,LGPL2.1}"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import androidx.compose.runtime.setValue
|
|||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import com.example.saludapp.ui.LoginScreen
|
import com.example.saludapp.ui.LoginScreen
|
||||||
|
import com.example.saludapp.ui.RegisterScreen
|
||||||
import com.example.saludapp.ui.StartScreen
|
import com.example.saludapp.ui.StartScreen
|
||||||
import com.example.saludapp.ui.theme.SaludappTheme
|
import com.example.saludapp.ui.theme.SaludappTheme
|
||||||
|
|
||||||
@@ -29,10 +30,14 @@ class MainActivity : ComponentActivity() {
|
|||||||
"start" -> StartScreen {
|
"start" -> StartScreen {
|
||||||
currentScreen = "login"
|
currentScreen = "login"
|
||||||
}
|
}
|
||||||
"login" -> LoginScreen {
|
"login" -> LoginScreen(
|
||||||
// Aquí iría la lógica tras login exitoso
|
onLoginSuccess = { currentScreen = "home" },
|
||||||
currentScreen = "home"
|
onNavigateToRegister = { currentScreen = "register" }
|
||||||
}
|
)
|
||||||
|
"register" -> RegisterScreen(
|
||||||
|
onRegisterSuccess = { currentScreen = "login" },
|
||||||
|
onBackToLogin = { currentScreen = "login" }
|
||||||
|
)
|
||||||
"home" -> {
|
"home" -> {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.example.saludapp.data
|
|||||||
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import java.security.MessageDigest
|
||||||
import java.sql.Connection
|
import java.sql.Connection
|
||||||
import java.sql.DriverManager
|
import java.sql.DriverManager
|
||||||
import java.sql.SQLException
|
import java.sql.SQLException
|
||||||
@@ -9,20 +10,27 @@ import java.sql.SQLException
|
|||||||
object DatabaseHelper {
|
object DatabaseHelper {
|
||||||
private const val URL = "jdbc:mariadb://mariadb.peta9.com:3306/saludapp_db"
|
private const val URL = "jdbc:mariadb://mariadb.peta9.com:3306/saludapp_db"
|
||||||
private const val USER = "saludapp_dba"
|
private const val USER = "saludapp_dba"
|
||||||
private const val PASSWORD = "TU_CONTRASEÑA_AQUÍ" // TODO: Reemplazar con la contraseña real
|
private const val PASSWORD = "Pedro@110387"
|
||||||
|
|
||||||
|
private fun hashPassword(password: String): String {
|
||||||
|
val bytes = password.toByteArray()
|
||||||
|
val md = MessageDigest.getInstance("SHA-256")
|
||||||
|
val digest = md.digest(bytes)
|
||||||
|
return digest.fold("") { str, it -> str + "%02x".format(it) }
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun validateLogin(user: String, pass: String): Boolean = withContext(Dispatchers.IO) {
|
suspend fun validateLogin(user: String, pass: String): Boolean = withContext(Dispatchers.IO) {
|
||||||
var connection: Connection? = null
|
var connection: Connection? = null
|
||||||
try {
|
try {
|
||||||
// Cargar el driver explícitamente si es necesario
|
|
||||||
Class.forName("org.mariadb.jdbc.Driver")
|
Class.forName("org.mariadb.jdbc.Driver")
|
||||||
connection = DriverManager.getConnection(URL, USER, PASSWORD)
|
connection = DriverManager.getConnection(URL, USER, PASSWORD)
|
||||||
|
|
||||||
|
val hashedPass = hashPassword(pass)
|
||||||
val query = "SELECT COUNT(*) FROM usuarios WHERE (username = ? OR email = ?) AND password = ?"
|
val query = "SELECT COUNT(*) FROM usuarios WHERE (username = ? OR email = ?) AND password = ?"
|
||||||
connection.prepareStatement(query).use { statement ->
|
connection.prepareStatement(query).use { statement ->
|
||||||
statement.setString(1, user)
|
statement.setString(1, user)
|
||||||
statement.setString(2, user)
|
statement.setString(2, user)
|
||||||
statement.setString(3, pass)
|
statement.setString(3, hashedPass)
|
||||||
|
|
||||||
val resultSet = statement.executeQuery()
|
val resultSet = statement.executeQuery()
|
||||||
if (resultSet.next()) {
|
if (resultSet.next()) {
|
||||||
@@ -40,4 +48,33 @@ object DatabaseHelper {
|
|||||||
}
|
}
|
||||||
return@withContext false
|
return@withContext false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun registerUser(user: String, email: String, pass: String, fullName: String): Boolean = withContext(Dispatchers.IO) {
|
||||||
|
var connection: Connection? = null
|
||||||
|
try {
|
||||||
|
Class.forName("org.mariadb.jdbc.Driver")
|
||||||
|
connection = DriverManager.getConnection(URL, USER, PASSWORD)
|
||||||
|
|
||||||
|
val hashedPass = hashPassword(pass)
|
||||||
|
val query = "INSERT INTO usuarios (username, email, password, nombre_completo) VALUES (?, ?, ?, ?)"
|
||||||
|
connection.prepareStatement(query).use { statement ->
|
||||||
|
statement.setString(1, user)
|
||||||
|
statement.setString(2, email)
|
||||||
|
statement.setString(3, hashedPass)
|
||||||
|
statement.setString(4, fullName)
|
||||||
|
|
||||||
|
val result = statement.executeUpdate()
|
||||||
|
return@withContext result > 0
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
connection?.close()
|
||||||
|
} catch (e: SQLException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return@withContext false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import com.example.saludapp.data.DatabaseHelper
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun LoginScreen(onLoginSuccess: () -> Unit) {
|
fun LoginScreen(onLoginSuccess: () -> Unit, onNavigateToRegister: () -> Unit) {
|
||||||
var email by remember { mutableStateOf("") }
|
var email by remember { mutableStateOf("") }
|
||||||
var password by remember { mutableStateOf("") }
|
var password by remember { mutableStateOf("") }
|
||||||
var isLoading by remember { mutableStateOf(false) }
|
var isLoading by remember { mutableStateOf(false) }
|
||||||
@@ -97,6 +97,10 @@ fun LoginScreen(onLoginSuccess: () -> Unit) {
|
|||||||
Text("Entrar")
|
Text("Entrar")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TextButton(onClick = onNavigateToRegister) {
|
||||||
|
Text("¿No tienes cuenta? Regístrate aquí")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,130 @@
|
|||||||
|
package com.example.saludapp.ui
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.material3.*
|
||||||
|
import androidx.compose.runtime.*
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.example.saludapp.data.DatabaseHelper
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun RegisterScreen(onRegisterSuccess: () -> Unit, onBackToLogin: () -> Unit) {
|
||||||
|
var fullName by remember { mutableStateOf("") }
|
||||||
|
var username by remember { mutableStateOf("") }
|
||||||
|
var email by remember { mutableStateOf("") }
|
||||||
|
var password by remember { mutableStateOf("") }
|
||||||
|
var isLoading by remember { mutableStateOf(false) }
|
||||||
|
var errorMessage by remember { mutableStateOf<String?>(null) }
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
|
Surface(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
color = MaterialTheme.colorScheme.background
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(24.dp),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
verticalArrangement = Arrangement.Center
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "Crear Cuenta",
|
||||||
|
style = MaterialTheme.typography.headlineMedium,
|
||||||
|
color = MaterialTheme.colorScheme.primary
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(24.dp))
|
||||||
|
|
||||||
|
OutlinedTextField(
|
||||||
|
value = fullName,
|
||||||
|
onValueChange = { fullName = it },
|
||||||
|
label = { Text("Nombre Completo") },
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
singleLine = true,
|
||||||
|
enabled = !isLoading
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(12.dp))
|
||||||
|
|
||||||
|
OutlinedTextField(
|
||||||
|
value = username,
|
||||||
|
onValueChange = { username = it },
|
||||||
|
label = { Text("Nombre de usuario") },
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
singleLine = true,
|
||||||
|
enabled = !isLoading
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(12.dp))
|
||||||
|
|
||||||
|
OutlinedTextField(
|
||||||
|
value = email,
|
||||||
|
onValueChange = { email = it },
|
||||||
|
label = { Text("Email") },
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
singleLine = true,
|
||||||
|
enabled = !isLoading
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(12.dp))
|
||||||
|
|
||||||
|
OutlinedTextField(
|
||||||
|
value = password,
|
||||||
|
onValueChange = { password = it },
|
||||||
|
label = { Text("Contraseña") },
|
||||||
|
visualTransformation = PasswordVisualTransformation(),
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
singleLine = true,
|
||||||
|
enabled = !isLoading
|
||||||
|
)
|
||||||
|
|
||||||
|
if (errorMessage != null) {
|
||||||
|
Text(
|
||||||
|
text = errorMessage!!,
|
||||||
|
color = MaterialTheme.colorScheme.error,
|
||||||
|
modifier = Modifier.padding(top = 16.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(32.dp))
|
||||||
|
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
scope.launch {
|
||||||
|
isLoading = true
|
||||||
|
errorMessage = null
|
||||||
|
val success = DatabaseHelper.registerUser(username, email, password, fullName)
|
||||||
|
isLoading = false
|
||||||
|
if (success) {
|
||||||
|
onRegisterSuccess()
|
||||||
|
} else {
|
||||||
|
errorMessage = "Error al registrar. El usuario puede que ya exista."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(56.dp),
|
||||||
|
enabled = !isLoading && fullName.isNotEmpty() && username.isNotEmpty() && email.isNotEmpty() && password.isNotEmpty()
|
||||||
|
) {
|
||||||
|
if (isLoading) {
|
||||||
|
CircularProgressIndicator(
|
||||||
|
modifier = Modifier.size(24.dp),
|
||||||
|
color = MaterialTheme.colorScheme.onPrimary,
|
||||||
|
strokeWidth = 2.dp
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Text("Registrarse")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TextButton(onClick = onBackToLogin) {
|
||||||
|
Text("¿Ya tienes cuenta? Inicia sesión")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user