diff --git a/backend/migrations/init_db.py b/backend/migrations/init_db.py index a44a30f..72d5826 100644 --- a/backend/migrations/init_db.py +++ b/backend/migrations/init_db.py @@ -1,100 +1 @@ -"""Initialize database with all tables and seed data.""" -import sqlite3 -from datetime import datetime -from passlib.context import CryptContext - -pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") - -def get_password_hash(password: str) -> str: - return pwd_context.hash(password) - -def init_db(): - """Create all tables and seed with initial users.""" - conn = sqlite3.connect('/app/data/family_hub.db') - cursor = conn.cursor() - - # Create users table with ALL fields - cursor.execute(""" - CREATE TABLE IF NOT EXISTS users ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - username VARCHAR(50) UNIQUE NOT NULL, - email VARCHAR(100) UNIQUE NOT NULL, - full_name VARCHAR(100) NOT NULL, - hashed_password VARCHAR(255) NOT NULL, - discord_id VARCHAR(100), - profile_picture VARCHAR(500), - is_active BOOLEAN DEFAULT 1, - is_admin BOOLEAN DEFAULT 0, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP - ) - """) - - # Create chores table - cursor.execute(""" - CREATE TABLE IF NOT EXISTS chores ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - title VARCHAR(200) NOT NULL, - description TEXT, - assigned_to INTEGER, - created_by INTEGER NOT NULL, - due_date DATE, - frequency VARCHAR(20) DEFAULT 'ON_TRIGGER', - status VARCHAR(20) DEFAULT 'pending', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY (assigned_to) REFERENCES users(id), - FOREIGN KEY (created_by) REFERENCES users(id) - ) - """) - - # Check if users already exist - cursor.execute("SELECT COUNT(*) FROM users") - user_count = cursor.fetchone()[0] - - if user_count == 0: - print("Seeding database with initial users...") - - # Create family members with default password - default_password_hash = get_password_hash("password123") - - users = [ - ("lou", "lou@family.local", "Lou", default_password_hash, False), - ("jess", "jess@family.local", "Jess", default_password_hash, True), - ("william", "william@family.local", "William", default_password_hash, False), - ("xander", "xander@family.local", "Xander", default_password_hash, False), - ("bella", "bella@family.local", "Bella", default_password_hash, False), - ] - - for username, email, full_name, password_hash, is_admin in users: - cursor.execute(""" - INSERT INTO users (username, email, full_name, hashed_password, is_active, is_admin) - VALUES (?, ?, ?, ?, 1, ?) - """, (username, email, full_name, password_hash, 1 if is_admin else 0)) - print(f" Created user: {username} (Admin: {is_admin})") - else: - print(f"Users table already contains {user_count} users, skipping seed.") - - conn.commit() - - # Verify tables - cursor.execute("SELECT name FROM sqlite_master WHERE type='table'") - tables = cursor.fetchall() - print(f"\nDatabase tables created: {[t[0] for t in tables]}") - - # Verify columns in users table - cursor.execute("PRAGMA table_info(users)") - columns = cursor.fetchall() - print("\nColumns in 'users' table:") - for col in columns: - print(f" - {col[1]} ({col[2]})") - - cursor.execute("SELECT COUNT(*) FROM users") - user_count = cursor.fetchone()[0] - print(f"\nTotal users in database: {user_count}") - - conn.close() - print("\nDatabase initialization complete!") - -if __name__ == "__main__": - init_db() +aW1wb3J0IHN5cwppbXBvcnQgb3MKZnJvbSBkYXRldGltZSBpbXBvcnQgZGF0ZXRpbWUsIHRpbWVkZWx0YQoKIyBBZGQgdGhlIGFwcCBkaXJlY3RvcnkgdG8gdGhlIHBhdGgKc3lzLnBhdGguaW5zZXJ0KDAsICcvYXBwJykKCmZyb20gc3FsYWxjaGVteSBpbXBvcnQgY3JlYXRlX2VuZ2luZQpmcm9tIHNxbGFsY2hlbXkub3JtIGltcG9ydCBzZXNzaW9ubWFrZXIKZnJvbSBhcHAuZGIuYmFzZSBpbXBvcnQgQmFzZQpmcm9tIGFwcC5tb2RlbHMudXNlciBpbXBvcnQgVXNlcgpmcm9tIGFwcC5tb2RlbHMuY2hvcmUgaW1wb3J0IENob3JlCmZyb20gYXBwLmNvcmUuc2VjdXJpdHkgaW1wb3J0IGdldF9wYXNzd29yZF9oYXNoCgojIERhdGFiYXNlIHNldHVwCkRBVEFCQVNFX1VSTCA9ICJzcWxpdGU6Ly8vLi9mYW1pbHlfaHViLmRiIgplbmdpbmUgPSBjcmVhdGVfZW5naW5lKERBVEFCQVNFX1VSTCwgY29ubmVjdF9hcmdzPXsiY2hlY2tfc2FtZV90aHJlYWQiOiBGYWxzZX0pClNlc3Npb25Mb2NhbCA9IHNlc3Npb25tYWtlcihhdXRvY29tbWl0PUZhbHNlLCBhdXRvZmx1c2g9RmFsc2UsIGJpbmQ9ZW5naW5lKQoKZGVmIGluaXRfZGIoKToKICAgICIiIkluaXRpYWxpemUgZGF0YWJhc2Ugd2l0aCBzY2hlbWEgYW5kIGRlbW8gZGF0YSIiIgogICAgcHJpbnQoIkNyZWF0aW5nIGFsbCB0YWJsZXMuLi4iKQogICAgQmFzZS5tZXRhZGF0YS5jcmVhdGVfYWxsKGJpbmQ9ZW5naW5lKQogICAgCiAgICBkYiA9IFNlc3Npb25Mb2NhbCgpCiAgICAKICAgIHRyeToKICAgICAgICAjIENoZWNrIGlmIHVzZXJzIGFscmVhZHkgZXhpc3QKICAgICAgICBleGlzdGluZ191c2VycyA9IGRiLnF1ZXJ5KFVzZXIpLmNvdW50KCkKICAgICAgICBpZiBleGlzdGluZ191c2VycyA+IDA6CiAgICAgICAgICAgIHByaW50KGYiRGF0YWJhc2UgYWxyZWFkeSBoYXMge2V4aXN0aW5nX3VzZXJzfSB1c2Vycy4gU2tpcHBpbmcgaW5pdGlhbGl6YXRpb24uIikKICAgICAgICAgICAgcmV0dXJuCiAgICAgICAgCiAgICAgICAgcHJpbnQoIkNyZWF0aW5nIGZhbWlseSBtZW1iZXJzLi4uIikKICAgICAgICAjIENyZWF0ZSBmYW1pbHkgbWVtYmVycwogICAgICAgIHVzZXJzX2RhdGEgPSBbCiAgICAgICAgICAgIHsidXNlcm5hbWUiOiAibG91IiwgImVtYWlsIjogImxvdUBmYW1pbHkubG9jYWwiLCAiZnVsbF9uYW1lIjogIkxvdSIsICJpc19hZG1pbiI6IEZhbHNlfSwKICAgICAgICAgICAgeyJ1c2VybmFtZSI6ICJqZXNzIiwgImVtYWlsIjogImplc3NAZmFtaWx5LmxvY2FsIiwgImZ1bGxfbmFtZSI6ICJKZXNzIiwgImlzX2FkbWluIjogVHJ1ZX0sCiAgICAgICAgICAgIHsidXNlcm5hbWUiOiAid2lsbGlhbSIsICJlbWFpbCI6ICJ3aWxsaWFtQGZhbWlseS5sb2NhbCIsICJmdWxsX25hbWUiOiAiV2lsbGlhbSIsICJpc19hZG1pbiI6IEZhbHNlfSwKICAgICAgICAgICAgeyJ1c2VybmFtZSI6ICJ4YW5kZXIiLCAiZW1haWwiOiAieGFuZGVyQGZhbWlseS5sb2NhbCIsICJmdWxsX25hbWUiOiAiWGFuZGVyIiwgImlzX2FkbWluIjogRmFsc2V9LAogICAgICAgICAgICB7InVzZXJuYW1lIjogImJlbGxhIiwgImVtYWlsIjogImJlbGxhQGZhbWlseS5sb2NhbCIsICJmdWxsX25hbWUiOiAiQmVsbGEiLCAiaXNfYWRtaW4iOiBGYWxzZX0sCiAgICAgICAgXQogICAgICAgIAogICAgICAgIHVzZXJzID0ge30KICAgICAgICBmb3IgdXNlcl9kYXRhIGluIHVzZXJzX2RhdGE6CiAgICAgICAgICAgIHVzZXIgPSBVc2VyKAogICAgICAgICAgICAgICAgdXNlcm5hbWU9dXNlcl9kYXRhWyJ1c2VybmFtZSJdLAogICAgICAgICAgICAgICAgZW1haWw9dXNlcl9kYXRhWyJlbWFpbCJdLAogICAgICAgICAgICAgICAgZnVsbF9uYW1lPXVzZXJfZGF0YVsiZnVsbF9uYW1lIl0sCiAgICAgICAgICAgICAgICBoYXNoZWRfcGFzc3dvcmQ9Z2V0X3Bhc3N3b3JkX2hhc2goInBhc3N3b3JkMTIzIiksCiAgICAgICAgICAgICAgICBpc19hZG1pbj11c2VyX2RhdGFbImlzX2FkbWluIl0sCiAgICAgICAgICAgICAgICBpc19hY3RpdmU9VHJ1ZSwKICAgICAgICAgICAgICAgIGRpc2NvcmRfaWQ9Tm9uZSwKICAgICAgICAgICAgICAgIHByb2ZpbGVfcGljdHVyZT1Ob25lCiAgICAgICAgICAgICkKICAgICAgICAgICAgZGIuYWRkKHVzZXIpCiAgICAgICAgICAgIHVzZXJzW3VzZXJfZGF0YVsidXNlcm5hbWUiXV0gPSB1c2VyCiAgICAgICAgICAgIHByaW50KGYiICDinJMgQ3JlYXRlZCB1c2VyOiB7dXNlcl9kYXRhWydmdWxsX25hbWUnXX0gKHt1c2VyX2RhdGFbJ3VzZXJuYW1lJ119KSIpCiAgICAgICAgCiAgICAgICAgZGIuY29tbWl0KCkKICAgICAgICAKICAgICAgICAjIFJlZnJlc2ggdG8gZ2V0IElEcwogICAgICAgIGZvciB1c2VyIGluIHVzZXJzLnZhbHVlcygpOgogICAgICAgICAgICBkYi5yZWZyZXNoKHVzZXIpCiAgICAgICAgCiAgICAgICAgcHJpbnQoIlxuQ3JlYXRpbmcgZGVtbyBjaG9yZXMuLi4iKQogICAgICAgICMgQ3JlYXRlIGRlbW8gY2hvcmVzIHdpdGggdmFyaW91cyBzdGF0dXNlcyBhbmQgYXNzaWdubWVudHMKICAgICAgICB0b2RheSA9IGRhdGV0aW1lLm5vdygpCiAgICAgICAgCiAgICAgICAgZGVtb19jaG9yZXMgPSBbCiAgICAgICAgICAgICMgRGFpbHkgY2hvcmVzCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICJ0aXRsZSI6ICJGZWVkIHRoZSBEb2ciLAogICAgICAgICAgICAgICAgImRlc2NyaXB0aW9uIjogIkdpdmUgUmV4IGhpcyBicmVha2Zhc3QgYW5kIGRpbm5lciIsCiAgICAgICAgICAgICAgICAicm9vbSI6ICJLaXRjaGVuIiwKICAgICAgICAgICAgICAgICJmcmVxdWVuY3kiOiAiREFJTFkiLAogICAgICAgICAgICAgICAgInN0YXR1cyI6ICJDT01QTEVURUQiLAogICAgICAgICAgICAgICAgImFzc2lnbmVkX3VzZXJfaWQiOiB1c2Vyc1sid2lsbGlhbSJdLmlkLAogICAgICAgICAgICAgICAgImR1ZV9kYXRlIjogdG9kYXkucmVwbGFjZShob3VyPTIzLCBtaW51dGU9NTksIHNlY29uZD01OSksCiAgICAgICAgICAgICAgICAiY29tcGxldGVkX2F0IjogdG9kYXkucmVwbGFjZShob3VyPTgsIG1pbnV0ZT0zMCkKICAgICAgICAgICAgfSwKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgInRpdGxlIjogIlRha2UgT3V0IFRyYXNoIiwKICAgICAgICAgICAgICAgICJkZXNjcmlwdGlvbiI6ICJFbXB0eSBhbGwgYmlucyBhbmQgdGFrZSB0byBjdXJiIiwKICAgICAgICAgICAgICAgICJyb29tIjogIktpdGNoZW4iLAogICAgICAgICAgICAgICAgImZyZXF1ZW5jeSI6ICJEQUlMWSIsCiAgICAgICAgICAgICAgICAic3RhdHVzIjogIlBFTkRJTkciLAogICAgICAgICAgICAgICAgImFzc2lnbmVkX3VzZXJfaWQiOiB1c2Vyc1sieGFuZGVyIl0uaWQsCiAgICAgICAgICAgICAgICAiZHVlX2RhdGUiOiB0b2RheS5yZXBsYWNlKGhvdXI9MjMsIG1pbnV0ZT01OSwgc2Vjb25kPTU5KSwKICAgICAgICAgICAgICAgICJjb21wbGV0ZWRfYXQiOiBOb25lCiAgICAgICAgICAgIH0sCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICJ0aXRsZSI6ICJXYXNoIERpc2hlcyIsCiAgICAgICAgICAgICAgICAiZGVzY3JpcHRpb24iOiAiTG9hZCBhbmQgcnVuIHRoZSBkaXNod2FzaGVyIiwKICAgICAgICAgICAgICAgICJyb29tIjogIktpdGNoZW4iLAogICAgICAgICAgICAgICAgImZyZXF1ZW5jeSI6ICJEQUlMWSIsCiAgICAgICAgICAgICAgICAic3RhdHVzIjogIklOX1BST0dSRVNTIiwKICAgICAgICAgICAgICAgICJhc3NpZ25lZF91c2VyX2lkIjogdXNlcnNbImJlbGxhIl0uaWQsCiAgICAgICAgICAgICAgICAiZHVlX2RhdGUiOiB0b2RheS5yZXBsYWNlKGhvdXI9MjMsIG1pbnV0ZT01OSwgc2Vjb25kPTU5KSwKICAgICAgICAgICAgICAgICJjb21wbGV0ZWRfYXQiOiBOb25lCiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICMgV2Vla2x5IGNob3JlcwogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICAidGl0bGUiOiAiVmFjdXVtIExpdmluZyBSb29tIiwKICAgICAgICAgICAgICAgICJkZXNjcmlwdGlvbiI6ICJWYWN1dW0gY2FycGV0cyBhbmQgdW5kZXIgZnVybml0dXJlIiwKICAgICAgICAgICAgICAgICJyb29tIjogIkxpdmluZyBSb29tIiwKICAgICAgICAgICAgICAgICJmcmVxdWVuY3kiOiAiV0VFS0xZIiwKICAgICAgICAgICAgICAgICJzdGF0dXMiOiAiUEVORElORyIsCiAgICAgICAgICAgICAgICAiYXNzaWduZWRfdXNlcl9pZCI6IHVzZXJzWyJsb3UiXS5pZCwKICAgICAgICAgICAgICAgICJkdWVfZGF0ZSI6ICh0b2RheSArIHRpbWVkZWx0YShkYXlzPTMpKS5yZXBsYWNlKGhvdXI9MjMsIG1pbnV0ZT01OSwgc2Vjb25kPTU5KSwKICAgICAgICAgICAgICAgICJjb21wbGV0ZWRfYXQiOiBOb25lCiAgICAgICAgICAgIH0sCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICJ0aXRsZSI6ICJDbGVhbiBCYXRocm9vbXMiLAogICAgICAgICAgICAgICAgImRlc2NyaXB0aW9uIjogIlNjcnViIHRvaWxldHMsIHNpbmtzLCBhbmQgc2hvd2VycyIsCiAgICAgICAgICAgICAgICAicm9vbSI6ICJCYXRocm9vbSIsCiAgICAgICAgICAgICAgICAiZnJlcXVlbmN5IjogIldFRUtMWSIsCiAgICAgICAgICAgICAgICAic3RhdHVzIjogIlBFTkRJTkciLAogICAgICAgICAgICAgICAgImFzc2lnbmVkX3VzZXJfaWQiOiB1c2Vyc1siamVzcyJdLmlkLAogICAgICAgICAgICAgICAgImR1ZV9kYXRlIjogKHRvZGF5ICsgdGltZWRlbHRhKGRheXM9MikpLnJlcGxhY2UoaG91cj0yMywgbWludXRlPTU5LCBzZWNvbmQ9NTkpLAogICAgICAgICAgICAgICAgImNvbXBsZXRlZF9hdCI6IE5vbmUKICAgICAgICAgICAgfSwKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgInRpdGxlIjogIk1vdyBMYXduIiwKICAgICAgICAgICAgICAgICJkZXNjcmlwdGlvbiI6ICJNb3cgZnJvbnQgYW5kIGJhY2sgeWFyZHMiLAogICAgICAgICAgICAgICAgInJvb20iOiAiWWFyZCIsCiAgICAgICAgICAgICAgICAiZnJlcXVlbmN5IjogIldFRUtMWSIsCiAgICAgICAgICAgICAgICAic3RhdHVzIjogIlBFTkRJTkciLAogICAgICAgICAgICAgICAgImFzc2lnbmVkX3VzZXJfaWQiOiB1c2Vyc1sid2lsbGlhbSJdLmlkLAogICAgICAgICAgICAgICAgImR1ZV9kYXRlIjogKHRvZGF5ICsgdGltZWRlbHRhKGRheXM9NSkpLnJlcGxhY2UoaG91cj0yMywgbWludXRlPTU5LCBzZWNvbmQ9NTkpLAogICAgICAgICAgICAgICAgImNvbXBsZXRlZF9hdCI6IE5vbmUKICAgICAgICAgICAgfSwKICAgICAgICAgICAgIyBNb250aGx5IGNob3JlcwogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICAidGl0bGUiOiAiQ2hhbmdlIEFpciBGaWx0ZXJzIiwKICAgICAgICAgICAgICAgICJkZXNjcmlwdGlvbiI6ICJSZXBsYWNlIEhWQUMgYWlyIGZpbHRlcnMgdGhyb3VnaG91dCBob3VzZSIsCiAgICAgICAgICAgICAgICAicm9vbSI6ICJVdGlsaXR5IFJvb20iLAogICAgICAgICAgICAgICAgImZyZXF1ZW5jeSI6ICJNT05USExZIiwKICAgICAgICAgICAgICAgICJzdGF0dXMiOiAiUEVORElORyIsCiAgICAgICAgICAgICAgICAiYXNzaWduZWRfdXNlcl9pZCI6IHVzZXJzWyJsb3UiXS5pZCwKICAgICAgICAgICAgICAgICJkdWVfZGF0ZSI6ICh0b2RheSArIHRpbWVkZWx0YShkYXlzPTE1KSkucmVwbGFjZShob3VyPTIzLCBtaW51dGU9NTksIHNlY29uZD01OSksCiAgICAgICAgICAgICAgICAiY29tcGxldGVkX2F0IjogTm9uZQogICAgICAgICAgICB9LAogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICAidGl0bGUiOiAiRGVlcCBDbGVhbiBGcmlkZ2UiLAogICAgICAgICAgICAgICAgImRlc2NyaXB0aW9uIjogIkVtcHR5LCB3aXBlIGRvd24gc2hlbHZlcywgY2hlY2sgZXhwaXJ5IGRhdGVzIiwKICAgICAgICAgICAgICAgICJyb29tIjogIktpdGNoZW4iLAogICAgICAgICAgICAgICAgImZyZXF1ZW5jeSI6ICJNT05USExZIiwKICAgICAgICAgICAgICAgICJzdGF0dXMiOiAiUEVORElORyIsCiAgICAgICAgICAgICAgICAiYXNzaWduZWRfdXNlcl9pZCI6IHVzZXJzWyJqZXNzIl0uaWQsCiAgICAgICAgICAgICAgICAiZHVlX2RhdGUiOiAodG9kYXkgKyB0aW1lZGVsdGEoZGF5cz0yMCkpLnJlcGxhY2UoaG91cj0yMywgbWludXRlPTU5LCBzZWNvbmQ9NTkpLAogICAgICAgICAgICAgICAgImNvbXBsZXRlZF9hdCI6IE5vbmUKICAgICAgICAgICAgfSwKICAgICAgICAgICAgIyBPbi10cmlnZ2VyIGNob3JlcyAobm8gc3BlY2lmaWMgc2NoZWR1bGUpCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICJ0aXRsZSI6ICJXYXRlciBQbGFudHMiLAogICAgICAgICAgICAgICAgImRlc2NyaXB0aW9uIjogIkNoZWNrIHNvaWwgbW9pc3R1cmUgYW5kIHdhdGVyIGFzIG5lZWRlZCIsCiAgICAgICAgICAgICAgICAicm9vbSI6ICJMaXZpbmcgUm9vbSIsCiAgICAgICAgICAgICAgICAiZnJlcXVlbmN5IjogIk9OX1RSSUdHRVIiLAogICAgICAgICAgICAgICAgInN0YXR1cyI6ICJQRU5ESU5HIiwKICAgICAgICAgICAgICAgICJhc3NpZ25lZF91c2VyX2lkIjogdXNlcnNbImJlbGxhIl0uaWQsCiAgICAgICAgICAgICAgICAiZHVlX2RhdGUiOiB0b2RheS5yZXBsYWNlKGhvdXI9MjMsIG1pbnV0ZT01OSwgc2Vjb25kPTU5KSwKICAgICAgICAgICAgICAgICJjb21wbGV0ZWRfYXQiOiBOb25lCiAgICAgICAgICAgIH0sCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICJ0aXRsZSI6ICJTb3J0IFJlY3ljbGluZyIsCiAgICAgICAgICAgICAgICAiZGVzY3JpcHRpb24iOiAiU2VwYXJhdGUgcmVjeWNsYWJsZXMgaW50byBwcm9wZXIgYmlucyIsCiAgICAgICAgICAgICAgICAicm9vbSI6ICJHYXJhZ2UiLAogICAgICAgICAgICAgICAgImZyZXF1ZW5jeSI6ICJPTl9UUklHR0VSIiwKICAgICAgICAgICAgICAgICJzdGF0dXMiOiAiQ09NUExFVEVEIiwKICAgICAgICAgICAgICAgICJhc3NpZ25lZF91c2VyX2lkIjogdXNlcnNbInhhbmRlciJdLmlkLAogICAgICAgICAgICAgICAgImR1ZV9kYXRlIjogKHRvZGF5IC0gdGltZWRlbHRhKGRheXM9MSkpLnJlcGxhY2UoaG91cj0yMywgbWludXRlPTU5LCBzZWNvbmQ9NTkpLAogICAgICAgICAgICAgICAgImNvbXBsZXRlZF9hdCI6ICh0b2RheSAtIHRpbWVkZWx0YShkYXlzPTEpKS5yZXBsYWNlKGhvdXI9MTQsIG1pbnV0ZT0yMCkKICAgICAgICAgICAgfSwKICAgICAgICAgICAgIyBTb21lIG92ZXJkdWUgY2hvcmVzCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICJ0aXRsZSI6ICJPcmdhbml6ZSBHYXJhZ2UiLAogICAgICAgICAgICAgICAgImRlc2NyaXB0aW9uIjogIlNvcnQgdG9vbHMgYW5kIGNsZWFuIHdvcmtzcGFjZSIsCiAgICAgICAgICAgICAgICAicm9vbSI6ICJHYXJhZ2UiLAogICAgICAgICAgICAgICAgImZyZXF1ZW5jeSI6ICJNT05USExZIiwKICAgICAgICAgICAgICAgICJzdGF0dXMiOiAiUEVORElORyIsCiAgICAgICAgICAgICAgICAiYXNzaWduZWRfdXNlcl9pZCI6IHVzZXJzWyJ3aWxsaWFtIl0uaWQsCiAgICAgICAgICAgICAgICAiZHVlX2RhdGUiOiAodG9kYXkgLSB0aW1lZGVsdGEoZGF5cz0zKSkucmVwbGFjZShob3VyPTIzLCBtaW51dGU9NTksIHNlY29uZD01OSksCiAgICAgICAgICAgICAgICAiY29tcGxldGVkX2F0IjogTm9uZQogICAgICAgICAgICB9LAogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICAidGl0bGUiOiAiQ2xlYW4gV2luZG93cyIsCiAgICAgICAgICAgICAgICAiZGVzY3JpcHRpb24iOiAiV2FzaCBhbGwgaW50ZXJpb3IgYW5kIGV4dGVyaW9yIHdpbmRvd3MiLAogICAgICAgICAgICAgICAgInJvb20iOiAiV2hvbGUgSG91c2UiLAogICAgICAgICAgICAgICAgImZyZXF1ZW5jeSI6ICJNT05USExZIiwKICAgICAgICAgICAgICAgICJzdGF0dXMiOiAiUEVORElORyIsCiAgICAgICAgICAgICAgICAiYXNzaWduZWRfdXNlcl9pZCI6IHVzZXJzWyJ4YW5kZXIiXS5pZCwKICAgICAgICAgICAgICAgICJkdWVfZGF0ZSI6ICh0b2RheSAtIHRpbWVkZWx0YShkYXlzPTEpKS5yZXBsYWNlKGhvdXI9MjMsIG1pbnV0ZT01OSwgc2Vjb25kPTU5KSwKICAgICAgICAgICAgICAgICJjb21wbGV0ZWRfYXQiOiBOb25lCiAgICAgICAgICAgIH0sCiAgICAgICAgXQogICAgICAgIAogICAgICAgIGZvciBjaG9yZV9kYXRhIGluIGRlbW9fY2hvcmVzOgogICAgICAgICAgICBjaG9yZSA9IENob3JlKCoqY2hvcmVfZGF0YSwgY3JlYXRlZF9ieT11c2Vyc1siamVzcyJdLmlkKQogICAgICAgICAgICBkYi5hZGQoY2hvcmUpCiAgICAgICAgICAgIHN0YXR1c19lbW9qaSA9ICLinJMiIGlmIGNob3JlX2RhdGFbInN0YXR1cyJdID09ICJDT01QTEVURUQiIGVsc2UgIuKPsyIgaWYgY2hvcmVfZGF0YVsic3RhdHVzIl0gPT0gIklOX1BST0dSRVNTIiBlbHNlICLil4siCiAgICAgICAgICAgIHByaW50KGYiICB7c3RhdHVzX2Vtb2ppfSB7Y2hvcmVfZGF0YVsndGl0bGUnXX0gLSB7Y2hvcmVfZGF0YVsnZnJlcXVlbmN5J119ICh7Y2hvcmVfZGF0YVsncm9vbSddfSkiKQogICAgICAgIAogICAgICAgIGRiLmNvbW1pdCgpCiAgICAgICAgcHJpbnQoZiJcbuKchSBEYXRhYmFzZSBpbml0aWFsaXplZCBzdWNjZXNzZnVsbHkgd2l0aCB7bGVuKHVzZXJzX2RhdGEpfSB1c2VycyBhbmQge2xlbihkZW1vX2Nob3Jlcyl9IGRlbW8gY2hvcmVzISIpCiAgICAgICAgCiAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgcHJpbnQoZiLinYwgRXJyb3IgaW5pdGlhbGl6aW5nIGRhdGFiYXNlOiB7ZX0iKQogICAgICAgIGRiLnJvbGxiYWNrKCkKICAgICAgICByYWlzZQogICAgZmluYWxseToKICAgICAgICBkYi5jbG9zZSgpCgppZiBfX25hbWVfXyA9PSAiX19tYWluX18iOgogICAgaW5pdF9kYigpCg== \ No newline at end of file