From b707251bf671f0d7e07c888446566b611c09ddad Mon Sep 17 00:00:00 2001 From: rimu <3310831+rimu@users.noreply.github.com> Date: Thu, 23 Nov 2023 15:10:44 +1300 Subject: [PATCH] special lemmy api endpoints for instance discovery --- app/activitypub/routes.py | 37 +- app/activitypub/util.py | 1060 +++++++++++++++++ app/models.py | 4 + ...1711_add_software_and_dates_to_instance.py | 38 + 4 files changed, 1137 insertions(+), 2 deletions(-) create mode 100644 migrations/versions/4a3ca1701711_add_software_and_dates_to_instance.py diff --git a/app/activitypub/routes.py b/app/activitypub/routes.py index 698903f9..82f3a802 100644 --- a/app/activitypub/routes.py +++ b/app/activitypub/routes.py @@ -9,9 +9,10 @@ from app.community.routes import show_community from app.user.routes import show_profile from app.constants import POST_TYPE_LINK, POST_TYPE_IMAGE from app.models import User, Community, CommunityJoinRequest, CommunityMember, CommunityBan, ActivityPubLog, Post, \ - PostReply, Instance, PostVote, PostReplyVote, File + PostReply, Instance, PostVote, PostReplyVote, File, AllowedInstances, BannedInstances from app.activitypub.util import public_key, users_total, active_half_year, active_month, local_posts, local_comments, \ - post_to_activity, find_actor_or_create, default_context, instance_blocked, find_reply_parent, find_liked_object + post_to_activity, find_actor_or_create, default_context, instance_blocked, find_reply_parent, find_liked_object, \ + lemmy_site_data from app.utils import gibberish, get_setting, is_image_url, allowlist_html, html_to_markdown, render_template, \ domain_from_url, markdown_to_html import werkzeug.exceptions @@ -74,6 +75,7 @@ def nodeinfo(): @bp.route('/nodeinfo/2.0') +@bp.route('/nodeinfo/2.0.json') def nodeinfo2(): nodeinfo_data = { @@ -99,6 +101,37 @@ def nodeinfo2(): return jsonify(nodeinfo_data) +@bp.route('/api/v3/site') +def lemmy_site(): + return jsonify(lemmy_site_data()) + + +@bp.route('/api/v3/federated_instances') +def lemmy_federated_instances(): + instances = Instance.query.all() + linked = [] + allowed = [] + blocked = [] + for instance in instances: + instance_data = {"id": instance.id, "domain": instance.domain, "published": instance.created_at.isoformat(), "updated": instance.updated_at.isoformat()} + if instance.software: + instance_data['software'] = instance.software + if instance.version: + instance_data['version'] = instance.version + linked.append(instance_data) + for instance in AllowedInstances.query.all(): + allowed.append({"id": instance.id, "domain": instance.domain, "published": datetime.utcnow(), "updated": datetime.utcnow()}) + for instance in BannedInstances.query.all(): + blocked.append({"id": instance.id, "domain": instance.domain, "published": datetime.utcnow(), "updated": datetime.utcnow()}) + return jsonify({ + "federated_instances": { + "linked": linked, + "allowed": allowed, + "blocked": blocked + } + }) + + @bp.route('/u/', methods=['GET']) def user_profile(actor): """ Requests to this endpoint can be for a JSON representation of the user, or a HTML rendering of their profile. diff --git a/app/activitypub/util.py b/app/activitypub/util.py index 5f3b7011..b9ed4c01 100644 --- a/app/activitypub/util.py +++ b/app/activitypub/util.py @@ -392,3 +392,1063 @@ def find_liked_object(ap_id) -> Union[Post, PostReply, None]: if post_reply: return post_reply return None + + +def lemmy_site_data(): + data = { + "site_view": { + "site": { + "id": 1, + "name": "PieFed", + "sidebar": "Rules:\n- [Don't be a dick](https://lemmy.nz/post/63098)\n\n[FAQ](https://lemmy.nz/post/31318) ~ [NZ Community List ](https://lemmy.nz/post/63156) ~ [Join Matrix chatroom](https://lemmy.nz/post/169187)", + "published": "2023-06-02T09:46:21.972257", + "updated": "2023-11-03T03:22:35.594456", + "icon": "https://lemmy.nz/pictrs/image/d308ef8d-4381-4a7a-b047-569ed5b8dd88.png", + "banner": "https://lemmy.nz/pictrs/image/68beebd5-4e01-44b6-bd4e-008b0d443ac1.png", + "description": "PieFed development", + "actor_id": "https://lemmy.nz/", + "last_refreshed_at": "2023-06-02T09:46:21.960383", + "inbox_url": "https://lemmy.nz/site_inbox", + "public_key": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx6cROxTmUbuWDHM3DcIx\nAWVy4O+cYlnMU3s89gbzhgVioPHqoajDbxNzVavqLd093ZhGPG6pEoGAGEgI9zG/\nnxpCcRC8uoMcu6Yh8E707VWRXFiXDsONyldBKnFmQouQDFAEmPaEOkYX3l1Qe6Q+\np4XKQRcD5hZWMvJVYpGsEa1euOcKrZvQffA+HQ1xcbU2Kts92ZiGkuXcEzOT8YR2\nX82Y/JkpeGkFlW4AociJ1ohfsH9i4OV+C215SgpCPxnEa9oEpluOvql8d7lg0yPA\nIisxtLb6hQtx5hiueILv7WB7kq1dh57RZQmvt7fuBsEk9rK5Lqc/ee9hxseqZKH8\nxwIDAQAB\n-----END PUBLIC KEY-----\n", + "instance_id": 1 + }, + "local_site": { + "id": 1, + "site_id": 1, + "site_setup": True, + "enable_downvotes": True, + "enable_nsfw": True, + "community_creation_admin_only": True, + "require_email_verification": True, + "application_question": "This is a New Zealand instance with a focus on New Zealand content. Most content can be accessed from any instance, see https://join-lemmy.org to find one that suits you.\n\nBecause of a Lemmy-wide spam issue, we have needed to turn on the requirement to apply for an account. We will approve you as soon as possible after reviewing your response.\n\nRemember if you didn't provide an email address, you won't be able to get notified you've been approved, so don't forget to check back.\n\nWhere are you from?", + "private_instance": False, + "default_theme": "browser", + "default_post_listing_type": "All", + "hide_modlog_mod_names": True, + "application_email_admins": True, + "actor_name_max_length": 20, + "federation_enabled": True, + "captcha_enabled": True, + "captcha_difficulty": "medium", + "published": "2023-06-02T09:46:22.153520", + "updated": "2023-11-03T03:22:35.600601", + "registration_mode": "RequireApplication", + "reports_email_admins": True + }, + "local_site_rate_limit": { + "id": 1, + "local_site_id": 1, + "message": 999, + "message_per_second": 60, + "post": 50, + "post_per_second": 600, + "register": 20, + "register_per_second": 3600, + "image": 100, + "image_per_second": 3600, + "comment": 100, + "comment_per_second": 600, + "search": 999, + "search_per_second": 600, + "published": "2023-06-02T09:46:22.156933" + }, + "counts": { + "id": 1, + "site_id": 1, + "users": 816, + "posts": 3017, + "comments": 19693, + "communities": 7, + "users_active_day": 21, + "users_active_week": 49, + "users_active_month": 85, + "users_active_half_year": 312 + } + }, + "admins": [ + { + "person": { + "id": 2, + "name": "Dave", + "avatar": "https://lemmy.nz/pictrs/image/5eb39c6b-a1f0-4cba-9832-40a5d8ffb76a.png", + "banned": False, + "published": "2023-06-02T09:46:20.302035", + "actor_id": "https://lemmy.nz/u/Dave", + "local": True, + "deleted": False, + "matrix_user_id": "@bechorin:matrix.org", + "admin": True, + "bot_account": False, + "instance_id": 1 + }, + "counts": { + "id": 1, + "person_id": 2, + "post_count": 165, + "post_score": 1442, + "comment_count": 2624, + "comment_score": 10207 + } + }, + { + "person": { + "id": 15059, + "name": "idanoo", + "banned": False, + "published": "2023-06-08T22:13:43.366681", + "actor_id": "https://lemmy.nz/u/idanoo", + "local": True, + "deleted": False, + "matrix_user_id": "@idanoo:mtrx.nz", + "admin": True, + "bot_account": False, + "instance_id": 1 + }, + "counts": { + "id": 6544, + "person_id": 15059, + "post_count": 0, + "post_score": 0, + "comment_count": 5, + "comment_score": 10 + } + } + ], + "version": "1.0.0", + "all_languages": [ + { + "id": 0, + "code": "und", + "name": "Undetermined" + }, + { + "id": 1, + "code": "aa", + "name": "Afaraf" + }, + { + "id": 2, + "code": "ab", + "name": "аҧсуа бызшәа" + }, + { + "id": 3, + "code": "ae", + "name": "avesta" + }, + { + "id": 4, + "code": "af", + "name": "Afrikaans" + }, + { + "id": 5, + "code": "ak", + "name": "Akan" + }, + { + "id": 6, + "code": "am", + "name": "አማርኛ" + }, + { + "id": 7, + "code": "an", + "name": "aragonés" + }, + { + "id": 8, + "code": "ar", + "name": "اَلْعَرَبِيَّةُ" + }, + { + "id": 9, + "code": "as", + "name": "অসমীয়া" + }, + { + "id": 10, + "code": "av", + "name": "авар мацӀ" + }, + { + "id": 11, + "code": "ay", + "name": "aymar aru" + }, + { + "id": 12, + "code": "az", + "name": "azərbaycan dili" + }, + { + "id": 13, + "code": "ba", + "name": "башҡорт теле" + }, + { + "id": 14, + "code": "be", + "name": "беларуская мова" + }, + { + "id": 15, + "code": "bg", + "name": "български език" + }, + { + "id": 16, + "code": "bi", + "name": "Bislama" + }, + { + "id": 17, + "code": "bm", + "name": "bamanankan" + }, + { + "id": 18, + "code": "bn", + "name": "বাংলা" + }, + { + "id": 19, + "code": "bo", + "name": "བོད་ཡིག" + }, + { + "id": 20, + "code": "br", + "name": "brezhoneg" + }, + { + "id": 21, + "code": "bs", + "name": "bosanski jezik" + }, + { + "id": 22, + "code": "ca", + "name": "Català" + }, + { + "id": 23, + "code": "ce", + "name": "нохчийн мотт" + }, + { + "id": 24, + "code": "ch", + "name": "Chamoru" + }, + { + "id": 25, + "code": "co", + "name": "corsu" + }, + { + "id": 26, + "code": "cr", + "name": "ᓀᐦᐃᔭᐍᐏᐣ" + }, + { + "id": 27, + "code": "cs", + "name": "čeština" + }, + { + "id": 28, + "code": "cu", + "name": "ѩзыкъ словѣньскъ" + }, + { + "id": 29, + "code": "cv", + "name": "чӑваш чӗлхи" + }, + { + "id": 30, + "code": "cy", + "name": "Cymraeg" + }, + { + "id": 31, + "code": "da", + "name": "dansk" + }, + { + "id": 32, + "code": "de", + "name": "Deutsch" + }, + { + "id": 33, + "code": "dv", + "name": "ދިވެހި" + }, + { + "id": 34, + "code": "dz", + "name": "རྫོང་ཁ" + }, + { + "id": 35, + "code": "ee", + "name": "Eʋegbe" + }, + { + "id": 36, + "code": "el", + "name": "Ελληνικά" + }, + { + "id": 37, + "code": "en", + "name": "English" + }, + { + "id": 38, + "code": "eo", + "name": "Esperanto" + }, + { + "id": 39, + "code": "es", + "name": "Español" + }, + { + "id": 40, + "code": "et", + "name": "eesti" + }, + { + "id": 41, + "code": "eu", + "name": "euskara" + }, + { + "id": 42, + "code": "fa", + "name": "فارسی" + }, + { + "id": 43, + "code": "ff", + "name": "Fulfulde" + }, + { + "id": 44, + "code": "fi", + "name": "suomi" + }, + { + "id": 45, + "code": "fj", + "name": "vosa Vakaviti" + }, + { + "id": 46, + "code": "fo", + "name": "føroyskt" + }, + { + "id": 47, + "code": "fr", + "name": "Français" + }, + { + "id": 48, + "code": "fy", + "name": "Frysk" + }, + { + "id": 49, + "code": "ga", + "name": "Gaeilge" + }, + { + "id": 50, + "code": "gd", + "name": "Gàidhlig" + }, + { + "id": 51, + "code": "gl", + "name": "galego" + }, + { + "id": 52, + "code": "gn", + "name": "Avañe'ẽ" + }, + { + "id": 53, + "code": "gu", + "name": "ગુજરાતી" + }, + { + "id": 54, + "code": "gv", + "name": "Gaelg" + }, + { + "id": 55, + "code": "ha", + "name": "هَوُسَ" + }, + { + "id": 56, + "code": "he", + "name": "עברית" + }, + { + "id": 57, + "code": "hi", + "name": "हिन्दी" + }, + { + "id": 58, + "code": "ho", + "name": "Hiri Motu" + }, + { + "id": 59, + "code": "hr", + "name": "Hrvatski" + }, + { + "id": 60, + "code": "ht", + "name": "Kreyòl ayisyen" + }, + { + "id": 61, + "code": "hu", + "name": "magyar" + }, + { + "id": 62, + "code": "hy", + "name": "Հայերեն" + }, + { + "id": 63, + "code": "hz", + "name": "Otjiherero" + }, + { + "id": 64, + "code": "ia", + "name": "Interlingua" + }, + { + "id": 65, + "code": "id", + "name": "Bahasa Indonesia" + }, + { + "id": 66, + "code": "ie", + "name": "Interlingue" + }, + { + "id": 67, + "code": "ig", + "name": "Asụsụ Igbo" + }, + { + "id": 68, + "code": "ii", + "name": "ꆈꌠ꒿ Nuosuhxop" + }, + { + "id": 69, + "code": "ik", + "name": "Iñupiaq" + }, + { + "id": 70, + "code": "io", + "name": "Ido" + }, + { + "id": 71, + "code": "is", + "name": "Íslenska" + }, + { + "id": 72, + "code": "it", + "name": "Italiano" + }, + { + "id": 73, + "code": "iu", + "name": "ᐃᓄᒃᑎᑐᑦ" + }, + { + "id": 74, + "code": "ja", + "name": "日本語" + }, + { + "id": 75, + "code": "jv", + "name": "basa Jawa" + }, + { + "id": 76, + "code": "ka", + "name": "ქართული" + }, + { + "id": 77, + "code": "kg", + "name": "Kikongo" + }, + { + "id": 78, + "code": "ki", + "name": "Gĩkũyũ" + }, + { + "id": 79, + "code": "kj", + "name": "Kuanyama" + }, + { + "id": 80, + "code": "kk", + "name": "қазақ тілі" + }, + { + "id": 81, + "code": "kl", + "name": "kalaallisut" + }, + { + "id": 82, + "code": "km", + "name": "ខេមរភាសា" + }, + { + "id": 83, + "code": "kn", + "name": "ಕನ್ನಡ" + }, + { + "id": 84, + "code": "ko", + "name": "한국어" + }, + { + "id": 85, + "code": "kr", + "name": "Kanuri" + }, + { + "id": 86, + "code": "ks", + "name": "कश्मीरी" + }, + { + "id": 87, + "code": "ku", + "name": "Kurdî" + }, + { + "id": 88, + "code": "kv", + "name": "коми кыв" + }, + { + "id": 89, + "code": "kw", + "name": "Kernewek" + }, + { + "id": 90, + "code": "ky", + "name": "Кыргызча" + }, + { + "id": 91, + "code": "la", + "name": "latine" + }, + { + "id": 92, + "code": "lb", + "name": "Lëtzebuergesch" + }, + { + "id": 93, + "code": "lg", + "name": "Luganda" + }, + { + "id": 94, + "code": "li", + "name": "Limburgs" + }, + { + "id": 95, + "code": "ln", + "name": "Lingála" + }, + { + "id": 96, + "code": "lo", + "name": "ພາສາລາວ" + }, + { + "id": 97, + "code": "lt", + "name": "lietuvių kalba" + }, + { + "id": 98, + "code": "lu", + "name": "Kiluba" + }, + { + "id": 99, + "code": "lv", + "name": "latviešu valoda" + }, + { + "id": 100, + "code": "mg", + "name": "fiteny malagasy" + }, + { + "id": 101, + "code": "mh", + "name": "Kajin M̧ajeļ" + }, + { + "id": 102, + "code": "mi", + "name": "te reo Māori" + }, + { + "id": 103, + "code": "mk", + "name": "македонски јазик" + }, + { + "id": 104, + "code": "ml", + "name": "മലയാളം" + }, + { + "id": 105, + "code": "mn", + "name": "Монгол хэл" + }, + { + "id": 106, + "code": "mr", + "name": "मराठी" + }, + { + "id": 107, + "code": "ms", + "name": "Bahasa Melayu" + }, + { + "id": 108, + "code": "mt", + "name": "Malti" + }, + { + "id": 109, + "code": "my", + "name": "ဗမာစာ" + }, + { + "id": 110, + "code": "na", + "name": "Dorerin Naoero" + }, + { + "id": 111, + "code": "nb", + "name": "Norsk bokmål" + }, + { + "id": 112, + "code": "nd", + "name": "isiNdebele" + }, + { + "id": 113, + "code": "ne", + "name": "नेपाली" + }, + { + "id": 114, + "code": "ng", + "name": "Owambo" + }, + { + "id": 115, + "code": "nl", + "name": "Nederlands" + }, + { + "id": 116, + "code": "nn", + "name": "Norsk nynorsk" + }, + { + "id": 117, + "code": "no", + "name": "Norsk" + }, + { + "id": 118, + "code": "nr", + "name": "isiNdebele" + }, + { + "id": 119, + "code": "nv", + "name": "Diné bizaad" + }, + { + "id": 120, + "code": "ny", + "name": "chiCheŵa" + }, + { + "id": 121, + "code": "oc", + "name": "occitan" + }, + { + "id": 122, + "code": "oj", + "name": "ᐊᓂᔑᓈᐯᒧᐎᓐ" + }, + { + "id": 123, + "code": "om", + "name": "Afaan Oromoo" + }, + { + "id": 124, + "code": "or", + "name": "ଓଡ଼ିଆ" + }, + { + "id": 125, + "code": "os", + "name": "ирон æвзаг" + }, + { + "id": 126, + "code": "pa", + "name": "ਪੰਜਾਬੀ" + }, + { + "id": 127, + "code": "pi", + "name": "पाऴि" + }, + { + "id": 128, + "code": "pl", + "name": "Polski" + }, + { + "id": 129, + "code": "ps", + "name": "پښتو" + }, + { + "id": 130, + "code": "pt", + "name": "Português" + }, + { + "id": 131, + "code": "qu", + "name": "Runa Simi" + }, + { + "id": 132, + "code": "rm", + "name": "rumantsch grischun" + }, + { + "id": 133, + "code": "rn", + "name": "Ikirundi" + }, + { + "id": 134, + "code": "ro", + "name": "Română" + }, + { + "id": 135, + "code": "ru", + "name": "Русский" + }, + { + "id": 136, + "code": "rw", + "name": "Ikinyarwanda" + }, + { + "id": 137, + "code": "sa", + "name": "संस्कृतम्" + }, + { + "id": 138, + "code": "sc", + "name": "sardu" + }, + { + "id": 139, + "code": "sd", + "name": "सिन्धी" + }, + { + "id": 140, + "code": "se", + "name": "Davvisámegiella" + }, + { + "id": 141, + "code": "sg", + "name": "yângâ tî sängö" + }, + { + "id": 142, + "code": "si", + "name": "සිංහල" + }, + { + "id": 143, + "code": "sk", + "name": "slovenčina" + }, + { + "id": 144, + "code": "sl", + "name": "slovenščina" + }, + { + "id": 145, + "code": "sm", + "name": "gagana fa'a Samoa" + }, + { + "id": 146, + "code": "sn", + "name": "chiShona" + }, + { + "id": 147, + "code": "so", + "name": "Soomaaliga" + }, + { + "id": 148, + "code": "sq", + "name": "Shqip" + }, + { + "id": 149, + "code": "sr", + "name": "српски језик" + }, + { + "id": 150, + "code": "ss", + "name": "SiSwati" + }, + { + "id": 151, + "code": "st", + "name": "Sesotho" + }, + { + "id": 152, + "code": "su", + "name": "Basa Sunda" + }, + { + "id": 153, + "code": "sv", + "name": "Svenska" + }, + { + "id": 154, + "code": "sw", + "name": "Kiswahili" + }, + { + "id": 155, + "code": "ta", + "name": "தமிழ்" + }, + { + "id": 156, + "code": "te", + "name": "తెలుగు" + }, + { + "id": 157, + "code": "tg", + "name": "тоҷикӣ" + }, + { + "id": 158, + "code": "th", + "name": "ไทย" + }, + { + "id": 159, + "code": "ti", + "name": "ትግርኛ" + }, + { + "id": 160, + "code": "tk", + "name": "Türkmençe" + }, + { + "id": 161, + "code": "tl", + "name": "Wikang Tagalog" + }, + { + "id": 162, + "code": "tn", + "name": "Setswana" + }, + { + "id": 163, + "code": "to", + "name": "faka Tonga" + }, + { + "id": 164, + "code": "tr", + "name": "Türkçe" + }, + { + "id": 165, + "code": "ts", + "name": "Xitsonga" + }, + { + "id": 166, + "code": "tt", + "name": "татар теле" + }, + { + "id": 167, + "code": "tw", + "name": "Twi" + }, + { + "id": 168, + "code": "ty", + "name": "Reo Tahiti" + }, + { + "id": 169, + "code": "ug", + "name": "ئۇيغۇرچە‎" + }, + { + "id": 170, + "code": "uk", + "name": "Українська" + }, + { + "id": 171, + "code": "ur", + "name": "اردو" + }, + { + "id": 172, + "code": "uz", + "name": "Ўзбек" + }, + { + "id": 173, + "code": "ve", + "name": "Tshivenḓa" + }, + { + "id": 174, + "code": "vi", + "name": "Tiếng Việt" + }, + { + "id": 175, + "code": "vo", + "name": "Volapük" + }, + { + "id": 176, + "code": "wa", + "name": "walon" + }, + { + "id": 177, + "code": "wo", + "name": "Wollof" + }, + { + "id": 178, + "code": "xh", + "name": "isiXhosa" + }, + { + "id": 179, + "code": "yi", + "name": "ייִדיש" + }, + { + "id": 180, + "code": "yo", + "name": "Yorùbá" + }, + { + "id": 181, + "code": "za", + "name": "Saɯ cueŋƅ" + }, + { + "id": 182, + "code": "zh", + "name": "中文" + }, + { + "id": 183, + "code": "zu", + "name": "isiZulu" + } + ], + "discussion_languages": [ + 0, + 37 + ], + "taglines": [ + { + "id": 19, + "local_site_id": 1, + "content": "Welcome to Lemmy NZ! [Don't be a dick](https://lemmy.nz/post/63098) ~ [FAQ](https://lemmy.nz/post/31318) ~ [NZ Community List ](https://lemmy.nz/post/63156) ~ [Join Matrix chatroom](https://lemmy.nz/post/169187)\n\n", + "published": "2023-06-28T09:53:58.605042" + } + ], + "custom_emojis": [] + } + return data diff --git a/app/models.py b/app/models.py index e6162a6b..bfc38134 100644 --- a/app/models.py +++ b/app/models.py @@ -472,6 +472,10 @@ class Instance(db.Model): shared_inbox = db.Column(db.String(256)) outbox = db.Column(db.String(256)) vote_weight = db.Column(db.Float, default=1.0) + software = db.Column(db.String(50)) + version = db.Column(db.String(50)) + created_at = db.Column(db.DateTime, default=datetime.utcnow) + updated_at = db.Column(db.DateTime, default=datetime.utcnow) class Settings(db.Model): diff --git a/migrations/versions/4a3ca1701711_add_software_and_dates_to_instance.py b/migrations/versions/4a3ca1701711_add_software_and_dates_to_instance.py new file mode 100644 index 00000000..29df8367 --- /dev/null +++ b/migrations/versions/4a3ca1701711_add_software_and_dates_to_instance.py @@ -0,0 +1,38 @@ +"""add software and dates to instance + +Revision ID: 4a3ca1701711 +Revises: 84a5cb2a5e5b +Create Date: 2023-11-23 14:33:06.928554 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '4a3ca1701711' +down_revision = '84a5cb2a5e5b' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('instance', schema=None) as batch_op: + batch_op.add_column(sa.Column('software', sa.String(length=50), nullable=True)) + batch_op.add_column(sa.Column('version', sa.String(length=50), nullable=True)) + batch_op.add_column(sa.Column('created_at', sa.DateTime(), nullable=True)) + batch_op.add_column(sa.Column('updated_at', sa.DateTime(), nullable=True)) + + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('instance', schema=None) as batch_op: + batch_op.drop_column('updated_at') + batch_op.drop_column('created_at') + batch_op.drop_column('version') + batch_op.drop_column('software') + + # ### end Alembic commands ###