Alternative ATProto PDS implementation
at oauth 23 kB view raw
1// Generated by diesel_ext 2 3#![allow(unused, non_snake_case)] 4#![allow(clippy::all)] 5 6pub mod pds { 7 8 #![allow(unnameable_types, unused_qualifications)] 9 use anyhow::{Result, bail}; 10 use chrono::DateTime; 11 use chrono::offset::Utc; 12 use diesel::backend::Backend; 13 use diesel::deserialize::FromSql; 14 use diesel::prelude::*; 15 use diesel::serialize::{Output, ToSql}; 16 use diesel::sql_types::Text; 17 use diesel::sqlite::Sqlite; 18 use diesel::*; 19 use serde::{Deserialize, Serialize}; 20 21 #[derive( 22 Queryable, 23 Identifiable, 24 Selectable, 25 Clone, 26 Debug, 27 PartialEq, 28 Default, 29 Serialize, 30 Deserialize, 31 )] 32 #[diesel(primary_key(request_uri))] 33 #[diesel(table_name = crate::schema::pds::oauth_par_requests)] 34 #[diesel(check_for_backend(Sqlite))] 35 pub struct OauthParRequest { 36 pub request_uri: String, 37 pub client_id: String, 38 pub response_type: String, 39 pub code_challenge: String, 40 pub code_challenge_method: String, 41 pub state: Option<String>, 42 pub login_hint: Option<String>, 43 pub scope: Option<String>, 44 pub redirect_uri: Option<String>, 45 pub response_mode: Option<String>, 46 pub display: Option<String>, 47 pub created_at: i64, 48 pub expires_at: i64, 49 } 50 51 #[derive( 52 Queryable, 53 Identifiable, 54 Selectable, 55 Clone, 56 Debug, 57 PartialEq, 58 Default, 59 Serialize, 60 Deserialize, 61 )] 62 #[diesel(primary_key(code))] 63 #[diesel(table_name = crate::schema::pds::oauth_authorization_codes)] 64 #[diesel(check_for_backend(Sqlite))] 65 pub struct OauthAuthorizationCode { 66 pub code: String, 67 pub client_id: String, 68 pub subject: String, 69 pub code_challenge: String, 70 pub code_challenge_method: String, 71 pub redirect_uri: String, 72 pub scope: Option<String>, 73 pub created_at: i64, 74 pub expires_at: i64, 75 pub used: bool, 76 } 77 78 #[derive( 79 Queryable, 80 Identifiable, 81 Selectable, 82 Clone, 83 Debug, 84 PartialEq, 85 Default, 86 Serialize, 87 Deserialize, 88 )] 89 #[diesel(primary_key(token))] 90 #[diesel(table_name = crate::schema::pds::oauth_refresh_tokens)] 91 #[diesel(check_for_backend(Sqlite))] 92 pub struct OauthRefreshToken { 93 pub token: String, 94 pub client_id: String, 95 pub subject: String, 96 pub dpop_thumbprint: String, 97 pub scope: Option<String>, 98 pub created_at: i64, 99 pub expires_at: i64, 100 pub revoked: bool, 101 } 102 103 #[derive( 104 Queryable, 105 Identifiable, 106 Selectable, 107 Clone, 108 Debug, 109 PartialEq, 110 Default, 111 Serialize, 112 Deserialize, 113 )] 114 #[diesel(primary_key(jti))] 115 #[diesel(table_name = crate::schema::pds::oauth_used_jtis)] 116 #[diesel(check_for_backend(Sqlite))] 117 pub struct OauthUsedJti { 118 pub jti: String, 119 pub issuer: String, 120 pub created_at: i64, 121 pub expires_at: i64, 122 } 123 124 #[derive( 125 Queryable, 126 Identifiable, 127 Selectable, 128 Clone, 129 Debug, 130 PartialEq, 131 Default, 132 Serialize, 133 Deserialize, 134 )] 135 #[diesel(primary_key(did))] 136 #[diesel(table_name = crate::schema::pds::account)] 137 #[diesel(check_for_backend(Sqlite))] 138 pub struct Account { 139 pub did: String, 140 pub email: String, 141 #[diesel(column_name = recoveryKey)] 142 #[serde(rename = "recoveryKey")] 143 pub recovery_key: Option<String>, 144 pub password: String, 145 #[diesel(column_name = createdAt)] 146 #[serde(rename = "createdAt")] 147 pub created_at: String, 148 #[diesel(column_name = invitesDisabled)] 149 #[serde(rename = "invitesDisabled")] 150 pub invites_disabled: i16, 151 #[diesel(column_name = emailConfirmedAt)] 152 #[serde(rename = "emailConfirmedAt")] 153 pub email_confirmed_at: Option<String>, 154 } 155 156 #[derive( 157 Queryable, 158 Identifiable, 159 Selectable, 160 Clone, 161 Debug, 162 PartialEq, 163 Default, 164 Serialize, 165 Deserialize, 166 )] 167 #[diesel(primary_key(did))] 168 #[diesel(table_name = crate::schema::pds::actor)] 169 #[diesel(check_for_backend(Sqlite))] 170 pub struct Actor { 171 pub did: String, 172 pub handle: Option<String>, 173 #[diesel(column_name = createdAt)] 174 #[serde(rename = "createdAt")] 175 pub created_at: String, 176 #[diesel(column_name = takedownRef)] 177 #[serde(rename = "takedownRef")] 178 pub takedown_ref: Option<String>, 179 #[diesel(column_name = deactivatedAt)] 180 #[serde(rename = "deactivatedAt")] 181 pub deactivated_at: Option<String>, 182 #[diesel(column_name = deleteAfter)] 183 #[serde(rename = "deleteAfter")] 184 pub delete_after: Option<String>, 185 } 186 187 #[derive( 188 Queryable, 189 Identifiable, 190 Selectable, 191 Clone, 192 Debug, 193 PartialEq, 194 Default, 195 Serialize, 196 Deserialize, 197 )] 198 #[diesel(primary_key(did, name))] 199 #[diesel(table_name = crate::schema::pds::app_password)] 200 #[diesel(check_for_backend(Sqlite))] 201 pub struct AppPassword { 202 pub did: String, 203 pub name: String, 204 pub password: String, 205 #[diesel(column_name = createdAt)] 206 #[serde(rename = "createdAt")] 207 pub created_at: String, 208 } 209 210 #[derive( 211 Queryable, 212 Identifiable, 213 Selectable, 214 Clone, 215 Debug, 216 PartialEq, 217 Default, 218 Serialize, 219 Deserialize, 220 )] 221 #[diesel(primary_key(did))] 222 #[diesel(table_name = crate::schema::pds::did_doc)] 223 #[diesel(check_for_backend(Sqlite))] 224 pub struct DidDoc { 225 pub did: String, 226 pub doc: String, 227 #[diesel(column_name = updatedAt)] 228 #[serde(rename = "updatedAt")] 229 pub updated_at: i64, 230 } 231 232 #[derive( 233 Clone, Copy, Debug, PartialEq, Eq, Hash, Default, Serialize, Deserialize, AsExpression, 234 )] 235 #[diesel(sql_type = Text)] 236 pub enum EmailTokenPurpose { 237 #[default] 238 ConfirmEmail, 239 UpdateEmail, 240 ResetPassword, 241 DeleteAccount, 242 PlcOperation, 243 } 244 245 impl EmailTokenPurpose { 246 pub fn as_str(&self) -> &'static str { 247 match self { 248 EmailTokenPurpose::ConfirmEmail => "confirm_email", 249 EmailTokenPurpose::UpdateEmail => "update_email", 250 EmailTokenPurpose::ResetPassword => "reset_password", 251 EmailTokenPurpose::DeleteAccount => "delete_account", 252 EmailTokenPurpose::PlcOperation => "plc_operation", 253 } 254 } 255 256 pub fn from_str(s: &str) -> Result<Self> { 257 match s { 258 "confirm_email" => Ok(EmailTokenPurpose::ConfirmEmail), 259 "update_email" => Ok(EmailTokenPurpose::UpdateEmail), 260 "reset_password" => Ok(EmailTokenPurpose::ResetPassword), 261 "delete_account" => Ok(EmailTokenPurpose::DeleteAccount), 262 "plc_operation" => Ok(EmailTokenPurpose::PlcOperation), 263 _ => bail!("Unable to parse as EmailTokenPurpose: `{s:?}`"), 264 } 265 } 266 } 267 268 impl<DB> Queryable<sql_types::Text, DB> for EmailTokenPurpose 269 where 270 DB: backend::Backend, 271 String: deserialize::FromSql<sql_types::Text, DB>, 272 { 273 type Row = String; 274 275 fn build(s: String) -> deserialize::Result<Self> { 276 Ok(Self::from_str(&s)?) 277 } 278 } 279 280 impl serialize::ToSql<sql_types::Text, sqlite::Sqlite> for EmailTokenPurpose 281 where 282 String: serialize::ToSql<sql_types::Text, sqlite::Sqlite>, 283 { 284 fn to_sql<'lifetime>( 285 &'lifetime self, 286 out: &mut serialize::Output<'lifetime, '_, sqlite::Sqlite>, 287 ) -> serialize::Result { 288 serialize::ToSql::<sql_types::Text, sqlite::Sqlite>::to_sql( 289 match self { 290 Self::ConfirmEmail => "confirm_email", 291 Self::UpdateEmail => "update_email", 292 Self::ResetPassword => "reset_password", 293 Self::DeleteAccount => "delete_account", 294 Self::PlcOperation => "plc_operation", 295 }, 296 out, 297 ) 298 } 299 } 300 301 #[derive( 302 Queryable, 303 Identifiable, 304 Selectable, 305 Clone, 306 Debug, 307 PartialEq, 308 Default, 309 Serialize, 310 Deserialize, 311 )] 312 #[diesel(primary_key(purpose, did))] 313 #[diesel(table_name = crate::schema::pds::email_token)] 314 #[diesel(check_for_backend(Sqlite))] 315 pub struct EmailToken { 316 pub purpose: EmailTokenPurpose, 317 pub did: String, 318 pub token: String, 319 #[diesel(column_name = requestedAt)] 320 #[serde(rename = "requestedAt")] 321 pub requested_at: String, 322 } 323 324 #[derive( 325 Queryable, 326 Identifiable, 327 Insertable, 328 Selectable, 329 Clone, 330 Debug, 331 PartialEq, 332 Default, 333 Serialize, 334 Deserialize, 335 )] 336 #[diesel(primary_key(code))] 337 #[diesel(table_name = crate::schema::pds::invite_code)] 338 #[diesel(check_for_backend(Sqlite))] 339 pub struct InviteCode { 340 pub code: String, 341 #[diesel(column_name = availableUses)] 342 #[serde(rename = "availableUses")] 343 pub available_uses: i32, 344 pub disabled: i16, 345 #[diesel(column_name = forAccount)] 346 #[serde(rename = "forAccount")] 347 pub for_account: String, 348 #[diesel(column_name = createdBy)] 349 #[serde(rename = "createdBy")] 350 pub created_by: String, 351 #[diesel(column_name = createdAt)] 352 #[serde(rename = "createdAt")] 353 pub created_at: String, 354 } 355 356 #[derive( 357 Queryable, 358 Identifiable, 359 Selectable, 360 Clone, 361 Debug, 362 PartialEq, 363 Default, 364 Serialize, 365 Deserialize, 366 )] 367 #[diesel(primary_key(code, usedBy))] 368 #[diesel(table_name = crate::schema::pds::invite_code_use)] 369 #[diesel(check_for_backend(Sqlite))] 370 pub struct InviteCodeUse { 371 pub code: String, 372 #[diesel(column_name = usedBy)] 373 #[serde(rename = "usedBy")] 374 pub used_by: String, 375 #[diesel(column_name = usedAt)] 376 #[serde(rename = "usedAt")] 377 pub used_at: String, 378 } 379 380 #[derive( 381 Queryable, 382 Identifiable, 383 Selectable, 384 Clone, 385 Debug, 386 PartialEq, 387 Default, 388 Serialize, 389 Deserialize, 390 )] 391 #[diesel(table_name = crate::schema::pds::refresh_token)] 392 #[diesel(check_for_backend(Sqlite))] 393 pub struct RefreshToken { 394 pub id: String, 395 pub did: String, 396 #[diesel(column_name = expiresAt)] 397 #[serde(rename = "expiresAt")] 398 pub expires_at: String, 399 #[diesel(column_name = nextId)] 400 #[serde(rename = "nextId")] 401 pub next_id: Option<String>, 402 #[diesel(column_name = appPasswordName)] 403 #[serde(rename = "appPasswordName")] 404 pub app_password_name: Option<String>, 405 } 406 407 #[derive( 408 Queryable, 409 Identifiable, 410 Selectable, 411 Insertable, 412 Clone, 413 Debug, 414 PartialEq, 415 Default, 416 Serialize, 417 Deserialize, 418 )] 419 #[diesel(primary_key(seq))] 420 #[diesel(table_name = crate::schema::pds::repo_seq)] 421 #[diesel(check_for_backend(Sqlite))] 422 pub struct RepoSeq { 423 #[diesel(deserialize_as = i64)] 424 pub seq: Option<i64>, 425 pub did: String, 426 #[diesel(column_name = eventType)] 427 #[serde(rename = "eventType")] 428 pub event_type: String, 429 #[diesel(sql_type = Bytea)] 430 pub event: Vec<u8>, 431 #[diesel(deserialize_as = i16)] 432 pub invalidated: Option<i16>, 433 #[diesel(column_name = sequencedAt)] 434 #[serde(rename = "sequencedAt")] 435 pub sequenced_at: String, 436 } 437 438 impl RepoSeq { 439 pub fn new(did: String, event_type: String, event: Vec<u8>, sequenced_at: String) -> Self { 440 RepoSeq { 441 did, 442 event_type, 443 event, 444 sequenced_at, 445 invalidated: None, // default values used on insert 446 seq: None, // default values used on insert 447 } 448 } 449 } 450 451 #[derive( 452 Queryable, 453 Identifiable, 454 Insertable, 455 Selectable, 456 Clone, 457 Debug, 458 PartialEq, 459 Default, 460 Serialize, 461 Deserialize, 462 )] 463 #[diesel(primary_key(id))] 464 #[diesel(table_name = crate::schema::pds::token)] 465 #[diesel(check_for_backend(Sqlite))] 466 pub struct Token { 467 pub id: String, 468 pub did: String, 469 #[diesel(column_name = tokenId)] 470 #[serde(rename = "tokenId")] 471 pub token_id: String, 472 #[diesel(column_name = createdAt)] 473 #[serde(rename = "createdAt")] 474 pub created_at: DateTime<Utc>, 475 #[diesel(column_name = updatedAt)] 476 #[serde(rename = "updatedAt")] 477 pub updated_at: DateTime<Utc>, 478 #[diesel(column_name = expiresAt)] 479 #[serde(rename = "expiresAt")] 480 pub expires_at: DateTime<Utc>, 481 #[diesel(column_name = clientId)] 482 #[serde(rename = "clientId")] 483 pub client_id: String, 484 #[diesel(column_name = clientAuth)] 485 #[serde(rename = "clientAuth")] 486 pub client_auth: String, 487 #[diesel(column_name = deviceId)] 488 #[serde(rename = "deviceId")] 489 pub device_id: Option<String>, 490 pub parameters: String, 491 pub details: Option<String>, 492 pub code: Option<String>, 493 #[diesel(column_name = currentRefreshToken)] 494 #[serde(rename = "currentRefreshToken")] 495 pub current_refresh_token: Option<String>, 496 } 497 498 #[derive( 499 Queryable, 500 Identifiable, 501 Insertable, 502 Selectable, 503 Clone, 504 Debug, 505 PartialEq, 506 Default, 507 Serialize, 508 Deserialize, 509 )] 510 #[diesel(primary_key(id))] 511 #[diesel(table_name = crate::schema::pds::device)] 512 #[diesel(check_for_backend(Sqlite))] 513 pub struct Device { 514 pub id: String, 515 #[diesel(column_name = sessionId)] 516 #[serde(rename = "sessionId")] 517 pub session_id: Option<String>, 518 #[diesel(column_name = userAgent)] 519 #[serde(rename = "userAgent")] 520 pub user_agent: Option<String>, 521 #[diesel(column_name = ipAddress)] 522 #[serde(rename = "ipAddress")] 523 pub ip_address: String, 524 #[diesel(column_name = lastSeenAt)] 525 #[serde(rename = "lastSeenAt")] 526 pub last_seen_at: DateTime<Utc>, 527 } 528 529 #[derive( 530 Queryable, 531 Identifiable, 532 Insertable, 533 Selectable, 534 Clone, 535 Debug, 536 PartialEq, 537 Default, 538 Serialize, 539 Deserialize, 540 )] 541 #[diesel(primary_key(did))] 542 #[diesel(table_name = crate::schema::pds::device_account)] 543 #[diesel(check_for_backend(Sqlite))] 544 pub struct DeviceAccount { 545 pub did: String, 546 #[diesel(column_name = deviceId)] 547 #[serde(rename = "deviceId")] 548 pub device_id: String, 549 #[diesel(column_name = authenticatedAt)] 550 #[serde(rename = "authenticatedAt")] 551 pub authenticated_at: DateTime<Utc>, 552 pub remember: bool, 553 #[diesel(column_name = authorizedClients)] 554 #[serde(rename = "authorizedClients")] 555 pub authorized_clients: String, 556 } 557 558 #[derive( 559 Queryable, 560 Identifiable, 561 Insertable, 562 Selectable, 563 Clone, 564 Debug, 565 PartialEq, 566 Default, 567 Serialize, 568 Deserialize, 569 )] 570 #[diesel(primary_key(id))] 571 #[diesel(table_name = crate::schema::pds::authorization_request)] 572 #[diesel(check_for_backend(Sqlite))] 573 pub struct AuthorizationRequest { 574 pub id: String, 575 pub did: Option<String>, 576 #[diesel(column_name = deviceId)] 577 #[serde(rename = "deviceId")] 578 pub device_id: Option<String>, 579 #[diesel(column_name = clientId)] 580 #[serde(rename = "clientId")] 581 pub client_id: String, 582 #[diesel(column_name = clientAuth)] 583 #[serde(rename = "clientAuth")] 584 pub client_auth: String, 585 pub parameters: String, 586 #[diesel(column_name = expiresAt)] 587 #[serde(rename = "expiresAt")] 588 pub expires_at: DateTime<Utc>, 589 pub code: Option<String>, 590 } 591 592 #[derive( 593 Queryable, Insertable, Selectable, Clone, Debug, PartialEq, Default, Serialize, Deserialize, 594 )] 595 #[diesel(table_name = crate::schema::pds::used_refresh_token)] 596 #[diesel(check_for_backend(Sqlite))] 597 pub struct UsedRefreshToken { 598 #[diesel(column_name = tokenId)] 599 #[serde(rename = "tokenId")] 600 pub token_id: String, 601 #[diesel(column_name = refreshToken)] 602 #[serde(rename = "refreshToken")] 603 pub refresh_token: String, 604 } 605} 606 607pub mod actor_store { 608 609 #![allow(unnameable_types, unused_qualifications)] 610 use anyhow::{Result, bail}; 611 use chrono::DateTime; 612 use chrono::offset::Utc; 613 use diesel::backend::Backend; 614 use diesel::deserialize::FromSql; 615 use diesel::prelude::*; 616 use diesel::serialize::{Output, ToSql}; 617 use diesel::sql_types::Text; 618 use diesel::sqlite::Sqlite; 619 use diesel::*; 620 use serde::{Deserialize, Serialize}; 621 622 #[derive( 623 Queryable, 624 Identifiable, 625 Insertable, 626 Selectable, 627 Clone, 628 Debug, 629 PartialEq, 630 Default, 631 Serialize, 632 Deserialize, 633 )] 634 #[diesel(table_name = crate::schema::actor_store::account_pref)] 635 #[diesel(check_for_backend(Sqlite))] 636 pub struct AccountPref { 637 pub id: i32, 638 pub name: String, 639 #[diesel(column_name = valueJson)] 640 #[serde(rename = "valueJson")] 641 pub value_json: Option<String>, 642 } 643 644 #[derive( 645 Queryable, 646 Identifiable, 647 Insertable, 648 Selectable, 649 Clone, 650 Debug, 651 PartialEq, 652 Default, 653 Serialize, 654 Deserialize, 655 )] 656 #[diesel(primary_key(uri, path))] 657 #[diesel(table_name = crate::schema::actor_store::backlink)] 658 #[diesel(check_for_backend(Sqlite))] 659 pub struct Backlink { 660 pub uri: String, 661 pub path: String, 662 #[diesel(column_name = linkTo)] 663 #[serde(rename = "linkTo")] 664 pub link_to: String, 665 } 666 667 #[derive( 668 Queryable, 669 Identifiable, 670 Selectable, 671 Clone, 672 Debug, 673 PartialEq, 674 Default, 675 Serialize, 676 Deserialize, 677 )] 678 #[diesel(treat_none_as_null = true)] 679 #[diesel(primary_key(cid))] 680 #[diesel(table_name = crate::schema::actor_store::blob)] 681 #[diesel(check_for_backend(Sqlite))] 682 pub struct Blob { 683 pub cid: String, 684 pub did: String, 685 #[diesel(column_name = mimeType)] 686 #[serde(rename = "mimeType")] 687 pub mime_type: String, 688 pub size: i32, 689 #[diesel(column_name = tempKey)] 690 #[serde(rename = "tempKey")] 691 pub temp_key: Option<String>, 692 pub width: Option<i32>, 693 pub height: Option<i32>, 694 #[diesel(column_name = createdAt)] 695 #[serde(rename = "createdAt")] 696 pub created_at: String, 697 #[diesel(column_name = takedownRef)] 698 #[serde(rename = "takedownRef")] 699 pub takedown_ref: Option<String>, 700 } 701 702 #[derive( 703 Queryable, 704 Identifiable, 705 Insertable, 706 Selectable, 707 Clone, 708 Debug, 709 PartialEq, 710 Default, 711 Serialize, 712 Deserialize, 713 )] 714 #[diesel(primary_key(uri))] 715 #[diesel(table_name = crate::schema::actor_store::record)] 716 #[diesel(check_for_backend(Sqlite))] 717 pub struct Record { 718 pub uri: String, 719 pub cid: String, 720 pub did: String, 721 pub collection: String, 722 pub rkey: String, 723 #[diesel(column_name = repoRev)] 724 #[serde(rename = "repoRev")] 725 pub repo_rev: Option<String>, 726 #[diesel(column_name = indexedAt)] 727 #[serde(rename = "indexedAt")] 728 pub indexed_at: String, 729 #[diesel(column_name = takedownRef)] 730 #[serde(rename = "takedownRef")] 731 pub takedown_ref: Option<String>, 732 } 733 734 #[derive( 735 QueryableByName, 736 Queryable, 737 Identifiable, 738 Selectable, 739 Clone, 740 Debug, 741 PartialEq, 742 Default, 743 Serialize, 744 Deserialize, 745 )] 746 #[diesel(primary_key(blobCid, recordUri))] 747 #[diesel(table_name = crate::schema::actor_store::record_blob)] 748 #[diesel(check_for_backend(Sqlite))] 749 pub struct RecordBlob { 750 #[diesel(column_name = blobCid, sql_type = Text)] 751 #[serde(rename = "blobCid")] 752 pub blob_cid: String, 753 #[diesel(column_name = recordUri, sql_type = Text)] 754 #[serde(rename = "recordUri")] 755 pub record_uri: String, 756 #[diesel(sql_type = Text)] 757 pub did: String, 758 } 759 760 #[derive( 761 Queryable, 762 Identifiable, 763 Selectable, 764 Insertable, 765 Clone, 766 Debug, 767 PartialEq, 768 Default, 769 Serialize, 770 Deserialize, 771 )] 772 #[diesel(primary_key(cid))] 773 #[diesel(table_name = crate::schema::actor_store::repo_block)] 774 #[diesel(check_for_backend(Sqlite))] 775 pub struct RepoBlock { 776 #[diesel(sql_type = Text)] 777 pub cid: String, 778 pub did: String, 779 #[diesel(column_name = repoRev)] 780 #[serde(rename = "repoRev")] 781 pub repo_rev: String, 782 pub size: i32, 783 #[diesel(sql_type = Bytea)] 784 pub content: Vec<u8>, 785 } 786 787 #[derive( 788 Queryable, 789 Identifiable, 790 Selectable, 791 Clone, 792 Debug, 793 PartialEq, 794 Default, 795 Serialize, 796 Deserialize, 797 )] 798 #[diesel(primary_key(did))] 799 #[diesel(table_name = crate::schema::actor_store::repo_root)] 800 #[diesel(check_for_backend(Sqlite))] 801 pub struct RepoRoot { 802 pub did: String, 803 pub cid: String, 804 pub rev: String, 805 #[diesel(column_name = indexedAt)] 806 #[serde(rename = "indexedAt")] 807 pub indexed_at: String, 808 } 809}