diff --git a/dns/Cargo.toml b/dns/Cargo.toml index 9ec9384..461ec79 100644 --- a/dns/Cargo.toml +++ b/dns/Cargo.toml @@ -11,7 +11,7 @@ jsonwebtoken = "9.2" bcrypt = "0.15" chrono = { version = "0.4", features = ["serde"] } colored = "2.1.0" -sqlx = { version = "0.7", features = ["runtime-tokio-rustls", "postgres", "chrono", "uuid", "migrate", "json"] } +sqlx = { version = "0.7", features = ["runtime-tokio-rustls", "mysql", "chrono", "uuid", "migrate", "json"] } anyhow = "1.0.86" futures = "0.3.30" macros-rs = "1.2.1" diff --git a/dns/config.template.toml b/dns/config.template.toml index 6cf8702..cfea29b 100644 --- a/dns/config.template.toml +++ b/dns/config.template.toml @@ -5,7 +5,7 @@ address = "127.0.0.1" port = 8085 [server.database] -url = "postgresql://username:password@localhost:5432/domains" +url = "mysql://root:password@localhost:3306/dns" # Maximum number of database connections max_connections = 10 diff --git a/dns/migrations/001_initial.sql b/dns/migrations/001_initial.sql index 4508380..ea922e2 100644 --- a/dns/migrations/001_initial.sql +++ b/dns/migrations/001_initial.sql @@ -1,5 +1,5 @@ CREATE TABLE IF NOT EXISTS users ( - id SERIAL PRIMARY KEY, + id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50) UNIQUE NOT NULL, password_hash VARCHAR(255) NOT NULL, registrations_remaining INTEGER DEFAULT 3, @@ -14,7 +14,7 @@ CREATE TABLE IF NOT EXISTS invite_codes ( code VARCHAR(32) UNIQUE NOT NULL, created_by INTEGER REFERENCES users(id), used_by INTEGER REFERENCES users(id), - created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, used_at TIMESTAMPTZ ); @@ -57,7 +57,7 @@ CREATE TABLE IF NOT EXISTS dns_records ( value VARCHAR(1000) NOT NULL, ttl INTEGER DEFAULT 3600, priority INTEGER, -- For MX records - created_at TIMESTAMPTZ DEFAULT NOW() + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX IF NOT EXISTS idx_dns_records_domain_type ON dns_records(domain_id, record_type); diff --git a/dns/migrations/002_remove_ip_requirement.sql b/dns/migrations/002_remove_ip_requirement.sql index cb83098..c79e8e4 100644 --- a/dns/migrations/002_remove_ip_requirement.sql +++ b/dns/migrations/002_remove_ip_requirement.sql @@ -1,5 +1,5 @@ -- Make IP column optional for domains -ALTER TABLE domains ALTER COLUMN ip DROP NOT NULL; +ALTER TABLE domains MODIFY COLUMN ip VARCHAR(255) NULL; -- Update DNS records constraint to only allow A, AAAA, CNAME, TXT ALTER TABLE dns_records DROP CONSTRAINT IF EXISTS dns_records_record_type_check; diff --git a/dns/migrations/003_add_ns_records.sql b/dns/migrations/003_add_ns_records.sql index fce2c17..5272c95 100644 --- a/dns/migrations/003_add_ns_records.sql +++ b/dns/migrations/003_add_ns_records.sql @@ -1,10 +1,10 @@ -- Re-add NS record support and extend record types -ALTER TABLE dns_records DROP CONSTRAINT IF EXISTS dns_records_record_type_check; +ALTER TABLE dns_records DROP CONSTRAINT dns_records_record_type_check; ALTER TABLE dns_records ADD CONSTRAINT dns_records_record_type_check CHECK (record_type IN ('A', 'AAAA', 'CNAME', 'TXT', 'NS', 'MX')); -- Add index for efficient NS record lookups during delegation -CREATE INDEX IF NOT EXISTS idx_dns_records_ns_lookup ON dns_records(record_type, name) WHERE record_type = 'NS'; +CREATE INDEX idx_dns_records_ns_lookup ON dns_records(record_type, name) WHERE record_type = 'NS'; -- Add index for subdomain resolution optimization -CREATE INDEX IF NOT EXISTS idx_dns_records_subdomain_lookup ON dns_records(domain_id, name, record_type); \ No newline at end of file +CREATE INDEX idx_dns_records_subdomain_lookup ON dns_records(domain_id, name, record_type); \ No newline at end of file diff --git a/dns/migrations/004_fix_record_types.sql b/dns/migrations/004_fix_record_types.sql index 55b863d..199fda4 100644 --- a/dns/migrations/004_fix_record_types.sql +++ b/dns/migrations/004_fix_record_types.sql @@ -4,5 +4,5 @@ ALTER TABLE dns_records ADD CONSTRAINT dns_records_record_type_check CHECK (record_type IN ('A', 'AAAA', 'CNAME', 'TXT', 'NS')); -- Add indexes for efficient DNS lookups if they don't exist -CREATE INDEX IF NOT EXISTS idx_dns_records_ns_lookup ON dns_records(record_type, name) WHERE record_type = 'NS'; -CREATE INDEX IF NOT EXISTS idx_dns_records_subdomain_lookup ON dns_records(domain_id, name, record_type); \ No newline at end of file +CREATE INDEX idx_dns_records_ns_lookup ON dns_records(record_type, name); +CREATE INDEX idx_dns_records_subdomain_lookup ON dns_records(domain_id, name, record_type); \ No newline at end of file diff --git a/dns/migrations/005_add_certificate_challenges.sql b/dns/migrations/005_add_certificate_challenges.sql index eef7d1e..90d024c 100644 --- a/dns/migrations/005_add_certificate_challenges.sql +++ b/dns/migrations/005_add_certificate_challenges.sql @@ -1,18 +1,18 @@ -- Add certificate challenges table for CA functionality CREATE TABLE IF NOT EXISTS certificate_challenges ( - id SERIAL PRIMARY KEY, + id INT AUTO_INCREMENT PRIMARY KEY, token VARCHAR(255) UNIQUE NOT NULL, domain VARCHAR(255) NOT NULL, - challenge_type VARCHAR(20) NOT NULL CHECK (challenge_type IN ('dns')), + challenge_type VARCHAR(20) NOT NULL, verification_data VARCHAR(500) NOT NULL, - status VARCHAR(20) DEFAULT 'pending' CHECK (status IN ('pending', 'valid', 'invalid', 'expired')), - created_at TIMESTAMPTZ DEFAULT NOW(), + status VARCHAR(20) DEFAULT 'pending', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, expires_at TIMESTAMPTZ NOT NULL ); -CREATE INDEX IF NOT EXISTS idx_certificate_challenges_token ON certificate_challenges(token); -CREATE INDEX IF NOT EXISTS idx_certificate_challenges_domain ON certificate_challenges(domain); -CREATE INDEX IF NOT EXISTS idx_certificate_challenges_expires_at ON certificate_challenges(expires_at); +CREATE INDEX idx_certificate_challenges_token ON certificate_challenges(token); +CREATE INDEX idx_certificate_challenges_domain ON certificate_challenges(domain); +CREATE INDEX idx_certificate_challenges_expires_at ON certificate_challenges(expires_at); -- Add table to store issued certificates CREATE TABLE IF NOT EXISTS issued_certificates ( @@ -21,13 +21,13 @@ CREATE TABLE IF NOT EXISTS issued_certificates ( user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, certificate_pem TEXT NOT NULL, private_key_pem TEXT NOT NULL, - issued_at TIMESTAMPTZ DEFAULT NOW(), + issued_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, expires_at TIMESTAMPTZ NOT NULL, revoked_at TIMESTAMPTZ, serial_number VARCHAR(255) UNIQUE NOT NULL ); -CREATE INDEX IF NOT EXISTS idx_issued_certificates_domain ON issued_certificates(domain); -CREATE INDEX IF NOT EXISTS idx_issued_certificates_user_id ON issued_certificates(user_id); -CREATE INDEX IF NOT EXISTS idx_issued_certificates_serial ON issued_certificates(serial_number); -CREATE INDEX IF NOT EXISTS idx_issued_certificates_expires_at ON issued_certificates(expires_at); \ No newline at end of file +CREATE INDEX idx_issued_certificates_domain ON issued_certificates(domain); +CREATE INDEX idx_issued_certificates_user_id ON issued_certificates(user_id); +CREATE INDEX idx_issued_certificates_serial ON issued_certificates(serial_number); +CREATE INDEX idx_issued_certificates_expires_at ON issued_certificates(expires_at); \ No newline at end of file diff --git a/dns/migrations/007_cleanup_invalid_records.sql b/dns/migrations/007_cleanup_invalid_records.sql index 65c8d58..f0ff418 100644 --- a/dns/migrations/007_cleanup_invalid_records.sql +++ b/dns/migrations/007_cleanup_invalid_records.sql @@ -2,6 +2,26 @@ DELETE FROM dns_records WHERE record_type NOT IN ('A', 'AAAA', 'CNAME', 'TXT'); -- Now apply the constraint -ALTER TABLE dns_records DROP CONSTRAINT IF EXISTS dns_records_record_type_check; -ALTER TABLE dns_records ADD CONSTRAINT dns_records_record_type_check - CHECK (record_type IN ('A', 'AAAA', 'CNAME', 'TXT')); \ No newline at end of file +ALTER TABLE dns_records DROP CONSTRAINT dns_records_record_type_check; + +-- MySQL doesn't support table-level CHECK constraints, using trigger instead +DELIMITER // +CREATE TRIGGER check_record_type_before_insert +BEFORE INSERT ON dns_records +FOR EACH ROW +BEGIN + IF NEW.record_type NOT IN ('A', 'AAAA', 'CNAME', 'TXT') THEN + SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Invalid record type'; + END IF; +END; +// +CREATE TRIGGER check_record_type_before_update +BEFORE UPDATE ON dns_records +FOR EACH ROW +BEGIN + IF NEW.record_type NOT IN ('A', 'AAAA', 'CNAME', 'TXT') THEN + SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Invalid record type'; + END IF; +END; +// +DELIMITER ; \ No newline at end of file diff --git a/dns/migrations/008_add_ca_storage.sql b/dns/migrations/008_add_ca_storage.sql index 266f3ef..d9a4024 100644 --- a/dns/migrations/008_add_ca_storage.sql +++ b/dns/migrations/008_add_ca_storage.sql @@ -1,8 +1,8 @@ -- Add table to store CA certificate and key CREATE TABLE IF NOT EXISTS ca_certificates ( - id SERIAL PRIMARY KEY, + id INT AUTO_INCREMENT PRIMARY KEY, ca_cert_pem TEXT NOT NULL, ca_key_pem TEXT NOT NULL, - created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - is_active BOOLEAN DEFAULT TRUE + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + is_active TINYINT(1) DEFAULT 1 ); \ No newline at end of file diff --git a/dns/migrations/009_add_csr_to_challenges.sql b/dns/migrations/009_add_csr_to_challenges.sql index 75d2996..10863ea 100644 --- a/dns/migrations/009_add_csr_to_challenges.sql +++ b/dns/migrations/009_add_csr_to_challenges.sql @@ -1,2 +1,2 @@ -- Add CSR field to certificate challenges -ALTER TABLE certificate_challenges ADD COLUMN IF NOT EXISTS csr_pem TEXT; \ No newline at end of file +ALTER TABLE certificate_challenges ADD COLUMN csr_pem TEXT; \ No newline at end of file diff --git a/dns/migrations/010_add_search_crawl_status.sql b/dns/migrations/010_add_search_crawl_status.sql index 6605012..cebd16d 100644 --- a/dns/migrations/010_add_search_crawl_status.sql +++ b/dns/migrations/010_add_search_crawl_status.sql @@ -1,28 +1,25 @@ -- Search engine domain crawl status tracking CREATE TABLE IF NOT EXISTS domain_crawl_status ( - domain_id INTEGER PRIMARY KEY REFERENCES domains(id) ON DELETE CASCADE, - last_crawled_at TIMESTAMPTZ, - next_crawl_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, + domain_id INT PRIMARY KEY, FOREIGN KEY (domain_id) REFERENCES domains(id) ON DELETE CASCADE, + last_crawled_at TIMESTAMP, + next_crawl_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, crawl_status VARCHAR(20) DEFAULT 'pending' CHECK (crawl_status IN ('pending', 'crawling', 'completed', 'failed', 'disabled')), error_message TEXT, pages_found INTEGER DEFAULT 0, - updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX IF NOT EXISTS idx_domain_crawl_status_next_crawl ON domain_crawl_status(next_crawl_at); CREATE INDEX IF NOT EXISTS idx_domain_crawl_status_status ON domain_crawl_status(crawl_status); -- Function to update the updated_at column -CREATE OR REPLACE FUNCTION update_updated_at_column() -RETURNS TRIGGER AS $$ -BEGIN - NEW.updated_at = CURRENT_TIMESTAMP; - RETURN NEW; -END; -$$ LANGUAGE plpgsql; - --- Trigger for updated_at -DROP TRIGGER IF EXISTS update_domain_crawl_status_updated_at ON domain_crawl_status; +-- MySQL trigger to update updated_at column +DELIMITER // CREATE TRIGGER update_domain_crawl_status_updated_at BEFORE UPDATE ON domain_crawl_status - FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); \ No newline at end of file + FOR EACH ROW +BEGIN + SET NEW.updated_at = CURRENT_TIMESTAMP; +END; +// +DELIMITER ; \ No newline at end of file diff --git a/dns/src/config/mod.rs b/dns/src/config/mod.rs index f357e77..ed4c164 100644 --- a/dns/src/config/mod.rs +++ b/dns/src/config/mod.rs @@ -3,7 +3,7 @@ mod structs; use colored::Colorize; use macros_rs::fmt::{crashln, string}; -use sqlx::{PgPool, Error}; +use sqlx::{MySqlPool, Error}; use std::fs::write; use structs::{Auth, Database, Server, Settings}; @@ -22,7 +22,7 @@ impl Config { address: "127.0.0.1".into(), port: 8080, database: Database { - url: "postgresql://username:password@localhost/domains".into(), + url: "mysql://root:password@localhost:3306/dns".into(), max_connections: 10, }, cert_path: "localhost+2.pem".into(), @@ -64,13 +64,13 @@ impl Config { return self; } - pub async fn connect_to_db(&self) -> Result { - let pool = PgPool::connect(&self.server.database.url).await?; + pub async fn connect_to_db(&self) -> Result { + let pool = MySqlPool::connect(&self.server.database.url).await?; // Run migrations sqlx::migrate!("./migrations").run(&pool).await?; - log::info!("PostgreSQL database connected"); + log::info!("MySQL database connected"); Ok(pool) } } diff --git a/dns/src/gurt_server/auth_routes.rs b/dns/src/gurt_server/auth_routes.rs index 9a85972..3526c60 100644 --- a/dns/src/gurt_server/auth_routes.rs +++ b/dns/src/gurt_server/auth_routes.rs @@ -200,8 +200,10 @@ pub(crate) async fn create_invite(_ctx: &ServerContext, app_state: AppState, cla let mut tx = app_state.db.begin().await .map_err(|_| GurtError::invalid_message("Database error"))?; - let affected_rows = sqlx::query("UPDATE users SET registrations_remaining = registrations_remaining - 1 WHERE id = $1 AND registrations_remaining > 0") - .bind(claims.user_id) + let affected_rows = sqlx::query( + "UPDATE users SET registrations_remaining = registrations_remaining - 1 WHERE id = ? AND registrations_remaining > 0" + ) + .bind(claims.user_id) .execute(&mut *tx) .await .map_err(|_| GurtError::invalid_message("Database error"))? @@ -214,7 +216,7 @@ pub(crate) async fn create_invite(_ctx: &ServerContext, app_state: AppState, cla })?); } - sqlx::query("INSERT INTO invite_codes (code, created_by, created_at) VALUES ($1, $2, $3)") + sqlx::query("INSERT INTO invite_codes (code, created_by, created_at) VALUES (?, ?, ?)") .bind(&invite_code) .bind(claims.user_id) .bind(Utc::now()) @@ -259,16 +261,16 @@ pub(crate) async fn redeem_invite(ctx: &ServerContext, app_state: AppState, clai let mut tx = app_state.db.begin().await .map_err(|_| GurtError::invalid_message("Database error"))?; - sqlx::query("UPDATE invite_codes SET used_by = $1, used_at = $2 WHERE id = $3") - .bind(claims.user_id) - .bind(Utc::now()) - .bind(invite.id) + sqlx::query("UPDATE invite_codes SET used_by = ?, used_at = ? WHERE id = ?") + .bind(claims.user_id) + .bind(Utc::now()) + .bind(invite.id) .execute(&mut *tx) .await .map_err(|_| GurtError::invalid_message("Database error"))?; - sqlx::query("UPDATE users SET registrations_remaining = registrations_remaining + 1 WHERE id = $1") - .bind(claims.user_id) + sqlx::query("UPDATE users SET registrations_remaining = registrations_remaining + 1 WHERE id = ?") + .bind(claims.user_id) .execute(&mut *tx) .await .map_err(|_| GurtError::invalid_message("Database error"))?; @@ -313,8 +315,10 @@ pub(crate) async fn create_domain_invite(_ctx: &ServerContext, app_state: AppSta let mut tx = app_state.db.begin().await .map_err(|_| GurtError::invalid_message("Database error"))?; - let affected_rows = sqlx::query("UPDATE users SET domain_invite_codes = domain_invite_codes - 1 WHERE id = $1 AND domain_invite_codes > 0") - .bind(claims.user_id) + let affected_rows = sqlx::query( + "UPDATE users SET domain_invite_codes = domain_invite_codes - 1 WHERE id = ? AND domain_invite_codes > 0" + ) + .bind(claims.user_id) .execute(&mut *tx) .await .map_err(|_| GurtError::invalid_message("Database error"))? @@ -367,16 +371,16 @@ pub(crate) async fn redeem_domain_invite(ctx: &ServerContext, app_state: AppStat let mut tx = app_state.db.begin().await .map_err(|_| GurtError::invalid_message("Database error"))?; - sqlx::query("UPDATE domain_invite_codes SET used_by = $1, used_at = $2 WHERE id = $3") - .bind(claims.user_id) - .bind(Utc::now()) - .bind(invite.id) + sqlx::query("UPDATE domain_invite_codes SET used_by = ?, used_at = ? WHERE id = ?") + .bind(claims.user_id) + .bind(Utc::now()) + .bind(invite.id) .execute(&mut *tx) .await .map_err(|_| GurtError::invalid_message("Database error"))?; - sqlx::query("UPDATE users SET domain_invite_codes = domain_invite_codes + 1 WHERE id = $1") - .bind(claims.user_id) + sqlx::query("UPDATE users SET domain_invite_codes = domain_invite_codes + 1 WHERE id = ?") + .bind(claims.user_id) .execute(&mut *tx) .await .map_err(|_| GurtError::invalid_message("Database error"))?; diff --git a/dns/src/gurt_server/routes.rs b/dns/src/gurt_server/routes.rs index 2d65ddf..18bae5e 100644 --- a/dns/src/gurt_server/routes.rs +++ b/dns/src/gurt_server/routes.rs @@ -78,9 +78,9 @@ pub(crate) async fn create_logic(domain: Domain, user_id: i32, app: &AppState) - } let existing_count: i64 = - sqlx::query_scalar("SELECT COUNT(*) FROM domains WHERE name = $1 AND tld = $2") - .bind(&domain.name) - .bind(&domain.tld) + sqlx::query_scalar("SELECT COUNT(*) FROM domains WHERE name = ? AND tld = ?") + .bind(&domain.name) + .bind(&domain.tld) .fetch_one(&app.db) .await .map_err(|_| GurtError::invalid_message("Database error"))?; @@ -89,7 +89,7 @@ pub(crate) async fn create_logic(domain: Domain, user_id: i32, app: &AppState) - return Err(GurtError::invalid_message("Domain already exists")); } - let user: (String,) = sqlx::query_as("SELECT username FROM users WHERE id = $1") + let user: (String,) = sqlx::query_as("SELECT username FROM users WHERE id = ?") .bind(user_id) .fetch_one(&app.db) .await @@ -98,7 +98,7 @@ pub(crate) async fn create_logic(domain: Domain, user_id: i32, app: &AppState) - let username = user.0; let domain_row: (i32,) = sqlx::query_as( - "INSERT INTO domains (name, tld, user_id, status) VALUES ($1, $2, $3, 'pending') RETURNING id" + "INSERT INTO domains (name, tld, user_id, status) VALUES (?, ?, ?, 'pending') RETURNING id" ) .bind(&domain.name) .bind(&domain.tld) @@ -110,7 +110,7 @@ pub(crate) async fn create_logic(domain: Domain, user_id: i32, app: &AppState) - let domain_id = domain_row.0; let affected_rows = sqlx::query( - "UPDATE users SET registrations_remaining = registrations_remaining - 1 WHERE id = $1 AND registrations_remaining > 0", + "UPDATE users SET registrations_remaining = registrations_remaining - 1 WHERE id = ? AND registrations_remaining > 0", ) .bind(user_id) .execute(&app.db) @@ -119,8 +119,8 @@ pub(crate) async fn create_logic(domain: Domain, user_id: i32, app: &AppState) - .rows_affected(); if affected_rows == 0 { - sqlx::query("DELETE FROM domains WHERE id = $1") - .bind(domain_id) + sqlx::query("DELETE FROM domains WHERE id = ?") + .bind(domain_id) .execute(&app.db) .await .map_err(|_| GurtError::invalid_message("Database cleanup error"))?; @@ -321,7 +321,7 @@ pub(crate) async fn delete_domain( return Ok(GurtResponse::not_found().with_string_body("Domain not found or access denied")); } - sqlx::query("DELETE FROM domains WHERE name = $1 AND tld = $2 AND user_id = $3") + sqlx::query("DELETE FROM domains WHERE name = ? AND tld = ? AND user_id = ?") .bind(name) .bind(tld) .bind(claims.user_id) @@ -620,7 +620,7 @@ pub(crate) async fn delete_domain_record( } }; - let rows_affected = sqlx::query("DELETE FROM dns_records WHERE id = $1 AND domain_id = $2") + let rows_affected = sqlx::query("DELETE FROM dns_records WHERE id = ? AND domain_id = ?") .bind(record_id) .bind(domain.id.unwrap()) .execute(&app_state.db) @@ -1066,7 +1066,7 @@ pub(crate) async fn get_certificate( }); // Delete the challenge as it's completed - sqlx::query("DELETE FROM certificate_challenges WHERE token = $1") + sqlx::query("DELETE FROM certificate_challenges WHERE token = ?") .bind(token) .execute(&app_state.db) .await