package deploy import ( "bytes" "context" "fmt" "net/url" "os" "path/filepath" "strings" "sync" "github.com/nbd-wtf/go-nostr" "github.com/studiokaiji/nostr-webhost/cmd/consts" "github.com/studiokaiji/nostr-webhost/cmd/keystore" "github.com/studiokaiji/nostr-webhost/cmd/relays" "github.com/studiokaiji/nostr-webhost/cmd/tools" "golang.org/x/net/html" ) func pathToKind(path string) (int, error) { splittedPath := strings.Split(path, ".") ex := splittedPath[len(splittedPath)-1] switch ex { case "html": return consts.KindWebhostHTML, nil case "css": return consts.KindWebhostCSS, nil case "js": return consts.KindWebhostJS, nil default: return 0, nil } } var nostrEventsQueue []*nostr.Event func addNostrEventQueue(event *nostr.Event) { nostrEventsQueue = append(nostrEventsQueue, event) } func publishEventsFromQueue() (string, error) { ctx := context.Background() fmt.Println("Publishing...") // リレーを取得 allRelays, err := relays.GetAllRelays() if err != nil { return "", err } // 各リレーに接続 var relays []*nostr.Relay for _, url := range allRelays { relay, err := nostr.RelayConnect(ctx, url) if err != nil { fmt.Println("❌ Failed to connect to:", url) continue } relays = append(relays, relay) } // Publishの進捗状況を表示 allEventsCount := len(nostrEventsQueue) uploadedFilesCount := 0 var wg sync.WaitGroup go func() { wg.Add(1) tools.DisplayProgressBar(&uploadedFilesCount, &allEventsCount) wg.Done() }() var mutex sync.Mutex // リレーへpublish for _, ev := range nostrEventsQueue { wg.Add(1) go func(event *nostr.Event) { for _, relay := range relays { _, err := relay.Publish(ctx, *event) if err != nil { fmt.Println(err) continue } } mutex.Lock() // ロックして排他制御 uploadedFilesCount++ // カウントアップ mutex.Unlock() // ロック解除 wg.Done() // ゴルーチンの終了を通知 }(ev) } wg.Wait() if uploadedFilesCount < allEventsCount { fmt.Println("Failed to deploy", allEventsCount-uploadedFilesCount, "files.") } indexEventId := nostrEventsQueue[len(nostrEventsQueue)-1].ID return indexEventId, err } func isExternalURL(urlStr string) bool { _, err := url.Parse(urlStr) if err != nil { return false } return false } func isValidFileType(str string) bool { return strings.HasSuffix(str, ".html") || strings.HasSuffix(str, ".css") || strings.HasSuffix(str, ".js") } func Deploy(basePath string) (string, error) { // 引数からデプロイしたいサイトのパスを受け取る。 filePath := filepath.Join(basePath, "index.html") // パスのディレクトリ内のファイルからindex.htmlファイルを取得 content, err := os.ReadFile(filePath) if err != nil { fmt.Println("❌ Failed to read index.html:", err) return "", err } // HTMLの解析 doc, err := html.Parse(bytes.NewReader(content)) if err != nil { fmt.Println("❌ Failed to parse index.html:", err) return "", nil } // Eventの取得に必要になるキーペアを取得 priKey, err := keystore.GetSecret() if err != nil { fmt.Println("❌ Failed to get private key:", err) return "", err } pubKey, err := nostr.GetPublicKey(priKey) if err != nil { fmt.Println("❌ Failed to get public key:", err) return "", err } // index.htmlファイル内に記述されている他Assetのパスから実際のデータを取得。 // 取得してきたデータをeventにしてIDを取得。 // リンクの解析と変換 convertLinks(priKey, pubKey, basePath, doc) // 更新されたHTML var buf bytes.Buffer html.Render(&buf, doc) strHtml := buf.String() // Eventを生成しキューに追加 event, err := getEvent(priKey, pubKey, strHtml, consts.KindWebhostHTML) if err != nil { fmt.Println("❌ Failed to get public key:", err) return "", err } addNostrEventQueue(event) fmt.Println("Added", filePath, "event to publish queue") return publishEventsFromQueue() } func convertLinks(priKey, pubKey, basePath string, n *html.Node) { //