/* * Copyright (C) 2024 Jonni Liljamo * * This file is licensed under AGPL-3.0-or-later, see NOTICE and LICENSE for * more information. */ package db import ( "errors" "git.src.quest/~liljamo/felu/internal/util" "github.com/oklog/ulid/v2" ) // DomainOwner contains a domains owners information. type DomainOwner struct { ID string Email string } // Domain contains a domains information. type Domain struct { ID string APIKey string Domain string A string AcmeChallenge string TsigKey string Owner DomainOwner } // FetchDomainsForUser fetches all domains for a specific user. func FetchDomainsForUser(userID string) ([]Domain, error) { rows, err := DBConn.Query(`SELECT id, apikey, ddns_domain, a_record, tsigkey FROM domains WHERE owner = $1`, userID) if err != nil { return nil, err } defer rows.Close() var domains []Domain for rows.Next() { var domain Domain err = rows.Scan(&domain.ID, &domain.APIKey, &domain.Domain, &domain.A, &domain.TsigKey) if err != nil { return nil, err } domains = append(domains, domain) } err = rows.Err() if err != nil { return nil, err } return domains, nil } // FetchAllDomains fetches all domains. func FetchAllDomains() ([]Domain, error) { rows, err := DBConn.Query(`SELECT id, ddns_domain, a_record, owner FROM domains`) if err != nil { return nil, err } defer rows.Close() var domains []Domain for rows.Next() { var domain Domain err = rows.Scan(&domain.ID, &domain.Domain, &domain.A, &domain.Owner.ID) if err != nil { return nil, err } ownerEmail, err := FetchUserEmail(domain.Owner.ID) if err != nil { return nil, err } domain.Owner.Email = ownerEmail domains = append(domains, domain) } err = rows.Err() if err != nil { return nil, err } return domains, nil } // CreateDomain creates a domains. func CreateDomain(domain string, aRecord string, owner string) error { ulid := ulid.Make().String() apikey := util.GenAPIKey() tsigKey := util.GenTsigKey() _, err := DBConn.Exec(`INSERT INTO domains(id, apikey, ddns_domain, a_record, tsigkey, owner) VALUES ($1, $2, $3, $4, $5, $6)`, ulid, apikey, domain, aRecord, tsigKey, owner) if err != nil { return err } return nil } // DeleteDomain deletes a domain. func DeleteDomain(id string, userID string) error { _, err := DBConn.Exec(`DELETE FROM domains WHERE id = $1 AND owner = $2`, id, userID) if err != nil { return err } return nil } // DeleteDomainsForUser deletes all domains for a user. func DeleteDomainsForUser(userID string) error { _, err := DBConn.Exec(`DELETE FROM domains WHERE owner = $1`, userID) if err != nil { return err } return nil } // FetchDomainARecord fetches the A record of a domain. func FetchDomainARecord(ddnsDomain string) (string, error) { var aRecord string err := DBConn.QueryRow(`SELECT a_record FROM domains WHERE ddns_domain = $1`, ddnsDomain).Scan(&aRecord) if err != nil { return "", err } return aRecord, nil } // UpdateDomainARecord updates the A record of a domain. func UpdateDomainARecord(ddnsDomain string, providedAPIKey string, aRecord string) error { var domainAPIKey string err := DBConn.QueryRow(`SELECT apikey FROM domains WHERE ddns_domain = $1`, ddnsDomain).Scan(&domainAPIKey) if err != nil { return err } if domainAPIKey != providedAPIKey { return errors.New("API key doesn't match") } _, err = DBConn.Exec(`UPDATE domains SET a_record = $1 WHERE ddns_domain = $2`, aRecord, ddnsDomain) if err != nil { return err } return nil } // UpdateDomainARecordManual updates the A record of a domain. func UpdateDomainARecordManual(id string, userID string, aRecord string) error { _, err := DBConn.Exec(`UPDATE domains SET a_record = $1 WHERE id = $2 AND owner = $3`, aRecord, id, userID) if err != nil { return err } return nil } // RefreshDomainAPIKey refreshes the API key of a domain. func RefreshDomainAPIKey(id string, userID string) error { apiKey := util.GenAPIKey() _, err := DBConn.Exec(`UPDATE domains SET apikey = $1 WHERE id = $2 AND owner = $3`, apiKey, id, userID) if err != nil { return err } return nil } // FetchDomainAcmeChallenge fetches the ACME challenge string of a domain. func FetchDomainAcmeChallenge(ddnsDomain string) (string, error) { var acmeChallengeString string err := DBConn.QueryRow(`SELECT acme_challenge FROM domains WHERE ddns_domain = $1`, ddnsDomain).Scan(&acmeChallengeString) if err != nil { return "", err } return acmeChallengeString, nil } // UpdateDomainAcmeChallenge updates the ACME challenge string of a domain. func UpdateDomainAcmeChallenge(ddnsDomain string, acmeChallengeString string) error { _, err := DBConn.Exec(`UPDATE domains SET acme_challenge = $1 WHERE ddns_domain = $2`, acmeChallengeString, ddnsDomain) if err != nil { return err } return nil } // DeleteDomainAcmeChallenge deletes the ACME challenge string of a domain. func DeleteDomainAcmeChallenge(ddnsDomain string) error { _, err := DBConn.Exec(`UPDATE domains SET acme_challenge = NULL WHERE ddns_domain = $1`, ddnsDomain) if err != nil { return err } return nil } // FetchDomainTsigKey fetches the tsig key of a domain. func FetchDomainTsigKey(ddnsDomain string) (string, error) { var tsig string err := DBConn.QueryRow(`SELECT tsigkey FROM domains WHERE ddns_domain = $1`, ddnsDomain).Scan(&tsig) if err != nil { return "", err } return tsig, nil } // RefreshDomainTsigKey refreshes the Tsig key of a domain. func RefreshDomainTsigKey(id string, userID string) error { tsigKey := util.GenTsigKey() _, err := DBConn.Exec(`UPDATE domains SET tsigkey = $1 WHERE id = $2 AND owner = $3`, tsigKey, id, userID) if err != nil { return err } return nil }