Mirror of @tangled.org/core. Running on a Raspberry Pi Zero 2 (Please be gentle).
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

appview/settings: add domain claim/release with r2, kv and db cleanup

Signed-off-by: Anirudh Oppiliappan <anirudh@tangled.org>

+98 -79
appview/pages/templates/user/settings/sites.html
+42 -38
appview/settings/settings.go
··· 1 1 package settings 2 2 3 3 import ( 4 + "context" 4 5 "database/sql" 5 6 "errors" 6 7 "fmt" 7 8 "log" 9 + "log/slog" 8 10 "net/http" 9 11 "net/url" 10 12 "strings" ··· 14 12 15 13 "github.com/go-chi/chi/v5" 16 14 "tangled.org/core/api/tangled" 15 + "tangled.org/core/appview/cloudflare" 17 16 "tangled.org/core/appview/config" 18 17 "tangled.org/core/appview/db" 19 18 "tangled.org/core/appview/email" ··· 22 19 "tangled.org/core/appview/models" 23 20 "tangled.org/core/appview/oauth" 24 21 "tangled.org/core/appview/pages" 22 + "tangled.org/core/appview/sites" 25 23 "tangled.org/core/tid" 26 24 27 25 comatproto "github.com/bluesky-social/indigo/api/atproto" ··· 33 29 ) 34 30 35 31 type Settings struct { 36 - Db *db.DB 37 - OAuth *oauth.OAuth 38 - Pages *pages.Pages 39 - Config *config.Config 32 + Db *db.DB 33 + OAuth *oauth.OAuth 34 + Pages *pages.Pages 35 + Config *config.Config 36 + CfClient *cloudflare.Client 37 + Logger *slog.Logger 40 38 } 41 39 42 40 func (s *Settings) Router() http.Handler { ··· 245 239 246 240 prefs, err := db.GetNotificationPreference(s.Db, did) 247 241 if err != nil { 248 - log.Printf("failed to get notification preferences: %s", err) 242 + s.Logger.Error("failed to get notification preferences", "err", err) 249 243 s.Pages.Notice(w, "settings-notifications-error", "Unable to load notification preferences.") 250 244 return 251 245 } ··· 275 269 276 270 err := s.Db.UpdateNotificationPreferences(r.Context(), prefs) 277 271 if err != nil { 278 - log.Printf("failed to update notification preferences: %s", err) 272 + s.Logger.Error("failed to update notification preferences", "err", err) 279 273 s.Pages.Notice(w, "settings-notifications-error", "Unable to save notification preferences.") 280 274 return 281 275 } ··· 287 281 user := s.OAuth.GetMultiAccountUser(r) 288 282 pubKeys, err := db.GetPublicKeysForDid(s.Db, user.Active.Did) 289 283 if err != nil { 290 - log.Println(err) 284 + s.Logger.Error("keys settings", "err", err) 291 285 } 292 286 293 287 s.Pages.UserKeysSettings(w, pages.UserKeysSettingsParams{ ··· 300 294 user := s.OAuth.GetMultiAccountUser(r) 301 295 emails, err := db.GetAllEmails(s.Db, user.Active.Did) 302 296 if err != nil { 303 - log.Println(err) 297 + s.Logger.Error("emails settings", "err", err) 304 298 } 305 299 306 300 s.Pages.UserEmailsSettings(w, pages.UserEmailsSettingsParams{ ··· 331 325 332 326 err := email.SendEmail(emailToSend) 333 327 if err != nil { 334 - log.Printf("sending email: %s", err) 328 + s.Logger.Error("sending email", "err", err) 335 329 s.Pages.Notice(w, "settings-emails-error", fmt.Sprintf("Unable to send verification email at this moment, try again later. %s", errorContext)) 336 330 return err 337 331 } ··· 343 337 switch r.Method { 344 338 case http.MethodGet: 345 339 s.Pages.Notice(w, "settings-emails", "Unimplemented.") 346 - log.Println("unimplemented") 340 + s.Logger.Warn("emails: unimplemented method") 347 341 return 348 342 case http.MethodPut: 349 343 did := s.OAuth.GetDid(r) ··· 358 352 // check if email already exists in database 359 353 existingEmail, err := db.GetEmail(s.Db, did, emAddr) 360 354 if err != nil && !errors.Is(err, sql.ErrNoRows) { 361 - log.Printf("checking for existing email: %s", err) 355 + s.Logger.Error("checking for existing email", "err", err) 362 356 s.Pages.Notice(w, "settings-emails-error", "Unable to add email at this moment, try again later.") 363 357 return 364 358 } ··· 378 372 // Begin transaction 379 373 tx, err := s.Db.Begin() 380 374 if err != nil { 381 - log.Printf("failed to start transaction: %s", err) 375 + s.Logger.Error("failed to start transaction", "err", err) 382 376 s.Pages.Notice(w, "settings-emails-error", "Unable to add email at this moment, try again later.") 383 377 return 384 378 } ··· 390 384 Verified: false, 391 385 VerificationCode: code, 392 386 }); err != nil { 393 - log.Printf("adding email: %s", err) 387 + s.Logger.Error("adding email", "err", err) 394 388 s.Pages.Notice(w, "settings-emails-error", "Unable to add email at this moment, try again later.") 395 389 return 396 390 } ··· 401 395 402 396 // Commit transaction 403 397 if err := tx.Commit(); err != nil { 404 - log.Printf("failed to commit transaction: %s", err) 398 + s.Logger.Error("failed to commit add-email transaction", "err", err) 405 399 s.Pages.Notice(w, "settings-emails-error", "Unable to add email at this moment, try again later.") 406 400 return 407 401 } ··· 416 410 // Begin transaction 417 411 tx, err := s.Db.Begin() 418 412 if err != nil { 419 - log.Printf("failed to start transaction: %s", err) 413 + s.Logger.Error("failed to start transaction", "err", err) 420 414 s.Pages.Notice(w, "settings-emails-error", "Unable to delete email at this moment, try again later.") 421 415 return 422 416 } 423 417 defer tx.Rollback() 424 418 425 419 if err := db.DeleteEmail(tx, did, emailAddr); err != nil { 426 - log.Printf("deleting email: %s", err) 420 + s.Logger.Error("deleting email", "err", err) 427 421 s.Pages.Notice(w, "settings-emails-error", "Unable to delete email at this moment, try again later.") 428 422 return 429 423 } 430 424 431 425 // Commit transaction 432 426 if err := tx.Commit(); err != nil { 433 - log.Printf("failed to commit transaction: %s", err) 427 + s.Logger.Error("failed to commit delete-email transaction", "err", err) 434 428 s.Pages.Notice(w, "settings-emails-error", "Unable to delete email at this moment, try again later.") 435 429 return 436 430 } ··· 460 454 461 455 valid, err := db.CheckValidVerificationCode(s.Db, did, emailAddr, code) 462 456 if err != nil { 463 - log.Printf("checking email verification: %s", err) 457 + s.Logger.Error("checking email verification", "err", err) 464 458 s.Pages.Notice(w, "settings-emails-error", "Error verifying email. Please try again later.") 465 459 return 466 460 } ··· 472 466 473 467 // Mark email as verified in the database 474 468 if err := db.MarkEmailVerified(s.Db, did, emailAddr); err != nil { 475 - log.Printf("marking email as verified: %s", err) 469 + s.Logger.Error("marking email as verified", "err", err) 476 470 s.Pages.Notice(w, "settings-emails-error", "Error updating email verification status. Please try again later.") 477 471 return 478 472 } ··· 501 495 if errors.Is(err, sql.ErrNoRows) { 502 496 s.Pages.Notice(w, "settings-emails-error", "Email not found. Please add it first.") 503 497 } else { 504 - log.Printf("checking for existing email: %s", err) 498 + s.Logger.Error("checking for existing email", "err", err) 505 499 s.Pages.Notice(w, "settings-emails-error", "Unable to resend verification email at this moment, try again later.") 506 500 } 507 501 return ··· 528 522 // Begin transaction 529 523 tx, err := s.Db.Begin() 530 524 if err != nil { 531 - log.Printf("failed to start transaction: %s", err) 525 + s.Logger.Error("failed to start transaction", "err", err) 532 526 s.Pages.Notice(w, "settings-emails-error", "Unable to resend verification email at this moment, try again later.") 533 527 return 534 528 } ··· 536 530 537 531 // Update the verification code and last sent time 538 532 if err := db.UpdateVerificationCode(tx, did, emAddr, code); err != nil { 539 - log.Printf("updating email verification: %s", err) 533 + s.Logger.Error("updating email verification code", "err", err) 540 534 s.Pages.Notice(w, "settings-emails-error", "Unable to resend verification email at this moment, try again later.") 541 535 return 542 536 } ··· 548 542 549 543 // Commit transaction 550 544 if err := tx.Commit(); err != nil { 551 - log.Printf("failed to commit transaction: %s", err) 545 + s.Logger.Error("failed to commit resend-verification transaction", "err", err) 552 546 s.Pages.Notice(w, "settings-emails-error", "Unable to resend verification email at this moment, try again later.") 553 547 return 554 548 } ··· 567 561 } 568 562 569 563 if err := db.MakeEmailPrimary(s.Db, did, emailAddr); err != nil { 570 - log.Printf("setting primary email: %s", err) 564 + s.Logger.Error("setting primary email", "err", err) 571 565 s.Pages.Notice(w, "settings-emails-error", "Error setting primary email. Please try again later.") 572 566 return 573 567 } ··· 579 573 switch r.Method { 580 574 case http.MethodGet: 581 575 s.Pages.Notice(w, "settings-keys", "Unimplemented.") 582 - log.Println("unimplemented") 576 + s.Logger.Warn("keys: unimplemented method") 583 577 return 584 578 case http.MethodPut: 585 579 did := s.OAuth.GetDid(r) ··· 594 588 595 589 _, _, _, _, err = ssh.ParseAuthorizedKey([]byte(key)) 596 590 if err != nil { 597 - log.Printf("parsing public key: %s", err) 591 + s.Logger.Error("parsing public key", "err", err) 598 592 s.Pages.Notice(w, "settings-keys", "That doesn't look like a valid public key. Make sure it's a <strong>public</strong> key.") 599 593 return 600 594 } ··· 603 597 604 598 tx, err := s.Db.Begin() 605 599 if err != nil { 606 - log.Printf("failed to start tx; adding public key: %s", err) 600 + s.Logger.Error("failed to start transaction for adding public key", "err", err) 607 601 s.Pages.Notice(w, "settings-keys", "Unable to add public key at this moment, try again later.") 608 602 return 609 603 } 610 604 defer tx.Rollback() 611 605 612 606 if err := db.AddPublicKey(tx, did, name, key, rkey); err != nil { 613 - log.Printf("adding public key: %s", err) 607 + s.Logger.Error("adding public key", "err", err) 614 608 s.Pages.Notice(w, "settings-keys", "Failed to add public key.") 615 609 return 616 610 } ··· 629 623 }) 630 624 // invalid record 631 625 if err != nil { 632 - log.Printf("failed to create record: %s", err) 626 + s.Logger.Error("failed to create atproto record", "err", err) 633 627 s.Pages.Notice(w, "settings-keys", "Failed to create record.") 634 628 return 635 629 } 636 630 637 - log.Println("created atproto record: ", resp.Uri) 631 + s.Logger.Info("created atproto record", "uri", resp.Uri) 638 632 639 633 err = tx.Commit() 640 634 if err != nil { 641 - log.Printf("failed to commit tx; adding public key: %s", err) 635 + s.Logger.Error("failed to commit add-key transaction", "err", err) 642 636 s.Pages.Notice(w, "settings-keys", "Unable to add public key at this moment, try again later.") 643 637 return 644 638 } ··· 654 648 rkey := q.Get("rkey") 655 649 key := q.Get("key") 656 650 657 - log.Println(name) 658 - log.Println(rkey) 659 - log.Println(key) 651 + s.Logger.Debug("deleting key", "name", name, "rkey", rkey, "key", key) 660 652 661 653 client, err := s.OAuth.AuthorizedClient(r) 662 654 if err != nil { 663 - log.Printf("failed to authorize client: %s", err) 655 + s.Logger.Error("failed to authorize client", "err", err) 664 656 s.Pages.Notice(w, "settings-keys", "Failed to authorize client.") 665 657 return 666 658 } 667 659 668 660 if err := db.DeletePublicKey(s.Db, did, name, key); err != nil { 669 - log.Printf("removing public key: %s", err) 661 + s.Logger.Error("removing public key", "err", err) 670 662 s.Pages.Notice(w, "settings-keys", "Failed to remove public key.") 671 663 return 672 664 } ··· 679 675 680 676 // invalid record 681 677 if err != nil { 682 - log.Printf("failed to delete record from PDS: %s", err) 678 + s.Logger.Error("failed to delete record from PDS", "err", err) 683 679 s.Pages.Notice(w, "settings-keys", "Failed to remove key from PDS.") 684 680 return 685 681 } 686 682 } 687 - log.Println("deleted successfully") 683 + s.Logger.Info("deleted key successfully", "name", name) 688 684 689 685 s.Pages.HxLocation(w, "/settings/keys") 690 686 return
+20 -20
appview/signup/signup.go
··· 14 14 15 15 "github.com/go-chi/chi/v5" 16 16 "github.com/posthog/posthog-go" 17 + "tangled.org/core/appview/cloudflare" 17 18 "tangled.org/core/appview/config" 18 19 "tangled.org/core/appview/db" 19 - "tangled.org/core/appview/dns" 20 20 "tangled.org/core/appview/email" 21 21 "tangled.org/core/appview/models" 22 22 "tangled.org/core/appview/pages" ··· 27 27 type Signup struct { 28 28 config *config.Config 29 29 db *db.DB 30 - cf *dns.Cloudflare 30 + cf *cloudflare.Client 31 31 posthog posthog.Client 32 32 idResolver *idresolver.Resolver 33 33 pages *pages.Pages ··· 36 36 } 37 37 38 38 func New(cfg *config.Config, database *db.DB, pc posthog.Client, idResolver *idresolver.Resolver, pages *pages.Pages, l *slog.Logger) *Signup { 39 - var cf *dns.Cloudflare 40 - if cfg.Cloudflare.ApiToken != "" && cfg.Cloudflare.ZoneId != "" { 39 + var cf *cloudflare.Client 40 + if cfg.Cloudflare.ApiToken != "" { 41 41 var err error 42 - cf, err = dns.NewCloudflare(cfg) 42 + cf, err = cloudflare.New(cfg) 43 43 if err != nil { 44 44 l.Warn("failed to create cloudflare client, signup will be disabled", "error", err) 45 45 } ··· 120 120 case http.MethodGet: 121 121 emailId := r.URL.Query().Get("id") 122 122 s.pages.Signup(w, pages.SignupParams{ 123 - CloudflareSiteKey: s.config.Cloudflare.TurnstileSiteKey, 123 + CloudflareSiteKey: s.config.Cloudflare.Turnstile.SiteKey, 124 124 EmailId: emailId, 125 125 }) 126 126 case http.MethodPost: ··· 284 284 285 285 // XXX: we have a wildcard *.tngl.sh record now 286 286 // step 2: create DNS record with actual DID 287 - // recordID, err = s.cf.CreateDNSRecord(ctx, dns.Record{ 288 - // Type: "TXT", 289 - // Name: "_atproto." + username, 290 - // Content: fmt.Sprintf(`"did=%s"`, did), 291 - // TTL: 6400, 292 - // Proxied: false, 293 - // }) 294 - // if err != nil { 295 - // s.l.Error("failed to create DNS record", "error", err) 296 - // s.pages.Notice(w, "signup-error", "Failed to create DNS record for your handle. Please contact support.") 297 - // return err 298 - // } 287 + // recordID, err = s.cf.CreateDNSRecord(ctx, cloudflare.DNSRecord{ 288 + // Type: "TXT", 289 + // Name: "_atproto." + username, 290 + // Content: fmt.Sprintf(`"did=%s"`, did), 291 + // TTL: 6400, 292 + // Proxied: false, 293 + // }) 294 + // if err != nil { 295 + // s.l.Error("failed to create DNS record", "error", err) 296 + // s.pages.Notice(w, "signup-error", "Failed to create DNS record for your handle. Please contact support.") 297 + // return err 298 + // } 299 299 300 300 // step 3: add email to database 301 301 err = db.AddEmail(s.db, models.Email{ ··· 358 358 return errors.New("captcha token is empty") 359 359 } 360 360 361 - if s.config.Cloudflare.TurnstileSecretKey == "" { 361 + if s.config.Cloudflare.Turnstile.SecretKey == "" { 362 362 return errors.New("turnstile secret key not configured") 363 363 } 364 364 365 365 data := url.Values{} 366 - data.Set("secret", s.config.Cloudflare.TurnstileSecretKey) 366 + data.Set("secret", s.config.Cloudflare.Turnstile.SecretKey) 367 367 data.Set("response", cfToken) 368 368 369 369 // include the client IP if we have it
+7 -4
appview/state/router.go
··· 210 210 211 211 func (s *State) SettingsRouter() http.Handler { 212 212 settings := &settings.Settings{ 213 - Db: s.db, 214 - OAuth: s.oauth, 215 - Pages: s.pages, 216 - Config: s.config, 213 + Db: s.db, 214 + OAuth: s.oauth, 215 + Pages: s.pages, 216 + Config: s.config, 217 + CfClient: s.cfClient, 218 + Logger: log.SubLogger(s.logger, "settings"), 217 219 } 218 220 219 221 return settings.Router() ··· 318 316 s.enforcer, 319 317 log.SubLogger(s.logger, "repo"), 320 318 s.validator, 319 + s.cfClient, 321 320 ) 322 321 return repo.Router(mw) 323 322 }
+29 -17
appview/state/state.go
··· 13 13 "tangled.org/core/api/tangled" 14 14 "tangled.org/core/appview" 15 15 "tangled.org/core/appview/bsky" 16 + "tangled.org/core/appview/cloudflare" 16 17 "tangled.org/core/appview/config" 17 18 "tangled.org/core/appview/db" 18 19 "tangled.org/core/appview/indexer" ··· 64 63 spindlestream *eventconsumer.Consumer 65 64 logger *slog.Logger 66 65 validator *validator.Validator 66 + cfClient *cloudflare.Client 67 67 } 68 68 69 69 func Make(ctx context.Context, config *config.Config) (*State, error) { ··· 174 172 notifier := notify.NewMergedNotifier(notifiers) 175 173 notifier = notify.NewLoggingNotifier(notifier, tlog.SubLogger(logger, "notify")) 176 174 177 - knotstream, err := Knotstream(ctx, config, d, enforcer, posthog, notifier) 175 + var cfClient *cloudflare.Client 176 + if config.Cloudflare.ApiToken != "" { 177 + cfClient, err = cloudflare.New(config) 178 + if err != nil { 179 + logger.Warn("failed to create cloudflare client, sites upload will be disabled", "err", err) 180 + cfClient = nil 181 + } 182 + } 183 + 184 + knotstream, err := Knotstream(ctx, config, d, enforcer, posthog, notifier, cfClient) 178 185 if err != nil { 179 186 return nil, fmt.Errorf("failed to start knotstream consumer: %w", err) 180 187 } ··· 196 185 spindlestream.Start(ctx) 197 186 198 187 state := &State{ 199 - d, 200 - notifier, 201 - indexer, 202 - oauth, 203 - enforcer, 204 - pages, 205 - res, 206 - mentionsResolver, 207 - posthog, 208 - jc, 209 - config, 210 - repoResolver, 211 - knotstream, 212 - spindlestream, 213 - logger, 214 - validator, 188 + db: d, 189 + notifier: notifier, 190 + indexer: indexer, 191 + oauth: oauth, 192 + enforcer: enforcer, 193 + pages: pages, 194 + idResolver: res, 195 + mentionsResolver: mentionsResolver, 196 + posthog: posthog, 197 + jc: jc, 198 + config: config, 199 + repoResolver: repoResolver, 200 + knotstream: knotstream, 201 + spindlestream: spindlestream, 202 + logger: logger, 203 + validator: validator, 204 + cfClient: cfClient, 215 205 } 216 206 217 207 // fetch initial bluesky posts if configured