diff --git a/README.md b/README.md index ffaf8f4..4c57c0d 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,56 @@ # DASLink -todo +DASLink is a simple tool to link ipfs content from [DAS](https://da.systems/). + +## How does it work? +Dependent on [DNSLink](https://docs.ipfs.io/concepts/dnslink/), [Cloudflare ipfs gateway](https://developers.cloudflare.com/distributed-web/ipfs-gateway), [Cloudflare DNS](https://api.cloudflare.com/#dns-records-for-a-zone-properties) and [das-database](https://github.com/DeAccountSystems/das-database). + +``` +┌───────────┐ ┌───────────┐ ┌────────────┐ +│ │ │ │ │ │ +│ Alice │ │ DNS │ │ipfs gateway│ +│ │ │ │ │ │ +└─────┬─────┘ └─────┬─────┘ └──────┬─────┘ + │ │ │ + │ visit alice.bit.cc │ │ + ├──────────────────────────►│ │ + │ │ CNAME point to │ + │ ├───────────────────────────►│ + │ │ │ + │ │◄───────────────────────────┤ + │ │ looking up the TXT record │ + │ ├───────────────────────────►│ + │ │ ├───────────┐ + │ │ │ │ + │ │ │ get the│ipfs content + │ │ │ │ + │ return ipfs content │ │◄──────────┘ + │◄──────────────────────────┼────────────────────────────┤ + │ │ │ + │ │ │ +┌─────┴─────┐ ┌─────┴─────┐ ┌──────┴─────┐ +│ │ │ │ │ │ +│ Alice │ │ DNS │ │ipfs gateway│ +│ │ │ │ │ │ +└───────────┘ └───────────┘ └────────────┘ +``` + +## Install +``` +# run das-database and keep it synchronized with the latest data +https://github.com/DeAccountSystems/das-database + +# get the code +git clone https://github.com/paicha/daslink.git + +# get your Cloudflare api tokens +https://dash.cloudflare.com/profile/api-tokens + +# edit config.yaml +cd config +cp config.yaml.sample config.yaml +vi config.yaml + +# compile and run +go build +./daslink +``` diff --git a/config/config.yaml.sample b/config/config.yaml.sample index ebd818f..701cea2 100644 --- a/config/config.yaml.sample +++ b/config/config.yaml.sample @@ -10,8 +10,8 @@ db: cloudflare: api_key: "" api_email: "" - zone_name: "bit.host" + zone_name: "bit.cc" ipfs: gateway: "cloudflare-ipfs.com" hostname: - suffix: ".host" + suffix: ".cc" diff --git a/dao/dao_records_info.go b/dao/dao_records_info.go index 4cc5f58..c938961 100644 --- a/dao/dao_records_info.go +++ b/dao/dao_records_info.go @@ -1,7 +1,6 @@ package dao import ( - "strings" "time" ) @@ -27,19 +26,16 @@ func (t *TableRecordsInfo) TableName() string { } func (d *DbDao) FindRecordInfoByKeys(keys []string) (recordInfo []TableRecordsInfo, err error) { - for i := 0; i < len(keys); i++ { - keys[i] = "`key` = " + "'" + keys[i] + "'" - } - err = d.db.Where(strings.Join(keys, " OR ")).Find(&recordInfo).Error + err = d.db.Where("`key` IN (?)", keys).Find(&recordInfo).Error return } func (d *DbDao) FindIpfsRecordInfoByMaxId(id uint64) (recordInfo []TableRecordsInfo, err error) { - err = d.db.Where("(`key` = 'ipfs' OR `key` = 'ipns') AND id > ? ", id).Find(&recordInfo).Error + err = d.db.Where("`key` IN(?) AND id > ? ", []string{"ipfs", "ipns"}, id).Find(&recordInfo).Error return } func (d *DbDao) FindIpfsRecordInfoByAccount(account string) (recordInfo []TableRecordsInfo, err error) { - err = d.db.Where("(`key` = 'ipfs' OR `key` = 'ipns') AND account = ? ", account).Find(&recordInfo).Error + err = d.db.Where("`key` IN(?) AND account = ? ", []string{"ipfs", "ipns"}, account).Find(&recordInfo).Error return } diff --git a/dns.go b/dns.go index 803fa24..b1409fa 100644 --- a/dns.go +++ b/dns.go @@ -162,23 +162,27 @@ func (d *DNSData) deleteAllInvalidDNSRecord(validAccounts []string) { } func (d *DNSData) deleteDNSRecord(record cloudflare.DNSRecord) { - d.api.DeleteDNSRecord(ctxServer, d.zoneID, record.ID) - if record.Type == "CNAME" { - for i, cnameRecord := range *d.cnameRecords { - if cnameRecord.ID == record.ID { - *d.cnameRecords = append((*d.cnameRecords)[:i], (*d.cnameRecords)[i+1:]...) - break + err := d.api.DeleteDNSRecord(ctxServer, d.zoneID, record.ID) + if err != nil { + log.Fatalf("cloudflare DeleteDNSRecord err:%s", err) + } else { + if record.Type == "CNAME" { + for i, cnameRecord := range *d.cnameRecords { + if cnameRecord.ID == record.ID { + *d.cnameRecords = append((*d.cnameRecords)[:i], (*d.cnameRecords)[i+1:]...) + break + } } - } - } else if record.Type == "TXT" { - for i, txtRecord := range *d.txtRecords { - if txtRecord.ID == record.ID { - *d.txtRecords = append((*d.txtRecords)[:i], (*d.txtRecords)[i+1:]...) - break + } else if record.Type == "TXT" { + for i, txtRecord := range *d.txtRecords { + if txtRecord.ID == record.ID { + *d.txtRecords = append((*d.txtRecords)[:i], (*d.txtRecords)[i+1:]...) + break + } } } + log.Debugf("Successfully delete %s record: %s", record.Type, record.Name) } - log.Debugf("Successfully delete %s record: %s", record.Type, record.Name) } func (d *DNSData) deleteDNSRecordByAccount(account string) { diff --git a/sync.go b/sync.go index 43157d2..bb1e263 100644 --- a/sync.go +++ b/sync.go @@ -4,20 +4,14 @@ import ( "daslink/dao" ) -var skipIpfsRecordIndex = []int{} +var skipIpfsRecordIndex = make(map[int]bool) func runSyncIpfsRecords(ipfsRecordList []dao.TableRecordsInfo, dnsData *DNSData, jobsChan chan string) { // batch update dns record validAccounts := []string{} for index, ipfsRecord := range ipfsRecordList { // skip the same records that have already been processed - skip := false - for _, sIndex := range skipIpfsRecordIndex { - if index == sIndex { - skip = true - break - } - } + _, skip := skipIpfsRecordIndex[index] if skip { continue } @@ -47,7 +41,7 @@ func findPriorityRecord(ipfsRecord dao.TableRecordsInfo, ipfsRecordList []dao.Ta if candidateRecord.Account == priorityRecord.Account { count += 1 if count > 1 { - skipIpfsRecordIndex = append(skipIpfsRecordIndex, index) + skipIpfsRecordIndex[index] = true if candidateRecord.Key == priorityRecord.Key { // select the first record if the key is the same if priorityRecord.Id > candidateRecord.Id { diff --git a/worker.go b/worker.go index 8132a5b..1afadf8 100644 --- a/worker.go +++ b/worker.go @@ -6,7 +6,7 @@ import ( "time" ) -var pollingAccountList = &[]string{} +var pollingAccounts = sync.Map{} func runWorker(wg *sync.WaitGroup, db *dao.DbDao, dnsData *DNSData, jobsChan chan string) { pollingChan := make(chan string, len(jobsChan)*2) @@ -21,7 +21,7 @@ func runWorker(wg *sync.WaitGroup, db *dao.DbDao, dnsData *DNSData, jobsChan cha ipfsRecordList, _ := db.FindIpfsRecordInfoByAccount(account) if len(ipfsRecordList) == 0 { dnsData.deleteDNSRecordByAccount(account) - removeFromPollingList(account) + pollingAccounts.Delete(account) } else { priorityRecord := findPriorityRecord(ipfsRecordList[0], ipfsRecordList) ipfsRecord, err := dnsData.updateDNSRecord(priorityRecord) @@ -34,16 +34,10 @@ func runWorker(wg *sync.WaitGroup, db *dao.DbDao, dnsData *DNSData, jobsChan cha }() } case account := <-jobsChan: - polling := false - for _, a := range *pollingAccountList { - if account == a { - polling = true - break - } - } + _, polling := pollingAccounts.Load(account) if !polling { pollingChan <- account - *pollingAccountList = append(*pollingAccountList, account) + pollingAccounts.Store(account, true) } case <-ctxServer.Done(): log.Info("worker exit") @@ -54,12 +48,3 @@ func runWorker(wg *sync.WaitGroup, db *dao.DbDao, dnsData *DNSData, jobsChan cha }() } } - -func removeFromPollingList(account string) { - for i, a := range *pollingAccountList { - if a == account { - *pollingAccountList = append((*pollingAccountList)[:i], (*pollingAccountList)[i+1:]...) - break - } - } -}