feat: use custom type to handle existing naive datetimes

This commit is contained in:
Iain Learmonth 2024-12-06 18:02:59 +00:00
parent e22abb383c
commit 39bdac1ecf
45 changed files with 210 additions and 84 deletions

View file

@ -3,24 +3,23 @@ import sys
from typing import Iterator from typing import Iterator
import yaml import yaml
from flask import Flask, redirect, url_for, send_from_directory from flask import Flask, redirect, send_from_directory, url_for
from flask.typing import ResponseReturnValue from flask.typing import ResponseReturnValue
from prometheus_client import make_wsgi_app, Metric, CollectorRegistry from prometheus_client import CollectorRegistry, Metric, make_wsgi_app
from prometheus_client.metrics_core import GaugeMetricFamily, CounterMetricFamily from prometheus_client.metrics_core import (CounterMetricFamily,
from prometheus_client.registry import Collector, REGISTRY GaugeMetricFamily)
from prometheus_client.registry import REGISTRY, Collector
from prometheus_flask_exporter import PrometheusMetrics
from sqlalchemy import text from sqlalchemy import text
from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.exc import SQLAlchemyError
from werkzeug.middleware.dispatcher import DispatcherMiddleware from werkzeug.middleware.dispatcher import DispatcherMiddleware
from app.api import api from app.api import api
from app.extensions import bootstrap from app.extensions import bootstrap, db, migrate
from app.extensions import db
from app.extensions import migrate
from app.models.automation import Automation, AutomationState from app.models.automation import Automation, AutomationState
from app.portal import portal from app.portal import portal
from app.portal.report import report from app.portal.report import report
from app.tfstate import tfstate from app.tfstate import tfstate
from prometheus_flask_exporter import PrometheusMetrics
app = Flask(__name__) app = Flask(__name__)
app.config.from_file("../config.yaml", load=yaml.safe_load) app.config.from_file("../config.yaml", load=yaml.safe_load)

View file

@ -1,18 +1,20 @@
import sys import sys
from datetime import datetime, timezone from datetime import datetime, timezone
from typing import List, TypedDict, NotRequired, Optional from typing import List, NotRequired, Optional, TypedDict
from cryptography import x509 from cryptography import x509
from flask import request, abort, jsonify, Blueprint from flask import Blueprint, abort, jsonify, request
from flask.typing import ResponseReturnValue from flask.typing import ResponseReturnValue
from sqlalchemy import exc from sqlalchemy import exc
from app.api.util import (DOMAIN_NAME_REGEX, MAX_ALLOWED_ITEMS,
MAX_DOMAIN_NAME_LENGTH, ListFilter,
get_single_resource, list_resources,
validate_description)
from app.extensions import db from app.extensions import db
from app.api.util import ListFilter, MAX_DOMAIN_NAME_LENGTH, DOMAIN_NAME_REGEX, list_resources, MAX_ALLOWED_ITEMS, \
validate_description, get_single_resource
from app.models.base import Group from app.models.base import Group
from app.models.onions import Onion from app.models.onions import Onion
from app.util.onion import onion_hostname, decode_onion_keys from app.util.onion import decode_onion_keys, onion_hostname
from app.util.x509 import validate_tls_keys from app.util.x509 import validate_tls_keys
api_onion = Blueprint('api_onion', __name__) api_onion = Blueprint('api_onion', __name__)

View file

@ -2,9 +2,9 @@ import base64
import binascii import binascii
import logging import logging
import re import re
from typing import Union, Any, Literal, Type, Callable, Dict, List, Optional from typing import Any, Callable, Dict, List, Literal, Optional, Type, Union
from flask import abort, request, jsonify from flask import abort, jsonify, request
from flask.typing import ResponseReturnValue from flask.typing import ResponseReturnValue
from sqlalchemy import BinaryExpression, ColumnElement, select from sqlalchemy import BinaryExpression, ColumnElement, select

View file

@ -1,10 +1,11 @@
from datetime import datetime, timedelta, timezone from datetime import datetime, timedelta, timezone
from typing import List from typing import List
from flask import Blueprint, request, abort from flask import Blueprint, abort, request
from flask.typing import ResponseReturnValue from flask.typing import ResponseReturnValue
from app.api.util import ListFilter, MAX_DOMAIN_NAME_LENGTH, DOMAIN_NAME_REGEX, list_resources, MAX_ALLOWED_ITEMS from app.api.util import (DOMAIN_NAME_REGEX, MAX_ALLOWED_ITEMS,
MAX_DOMAIN_NAME_LENGTH, ListFilter, list_resources)
from app.models.base import Group from app.models.base import Group
from app.models.mirrors import Origin, Proxy from app.models.mirrors import Origin, Proxy

View file

@ -3,6 +3,7 @@ Bypass Censorship Resource Names.
""" """
from __future__ import annotations from __future__ import annotations
from dataclasses import dataclass, field from dataclasses import dataclass, field
from typing import Any from typing import Any

View file

@ -9,15 +9,15 @@ from typing import Any, Callable, Dict, List, Type
from sqlalchemy import inspect from sqlalchemy import inspect
from app import app from app import app
from app.cli import _SubparserType, BaseCliHandler from app.cli import BaseCliHandler, _SubparserType
from app.extensions import db from app.extensions import db
from app.models.activity import Webhook, Activity from app.models.activity import Activity, Webhook
from app.models.automation import AutomationLogs, Automation, AutomationState from app.models.alarms import Alarm, AlarmState
from app.models.base import Group, MirrorList, PoolGroup, Pool from app.models.automation import Automation, AutomationLogs, AutomationState
from app.models.base import Group, MirrorList, Pool, PoolGroup
from app.models.bridges import Bridge, BridgeConf from app.models.bridges import Bridge, BridgeConf
from app.models.mirrors import Origin, Proxy, SmartProxy from app.models.mirrors import Origin, Proxy, SmartProxy
from app.models.alarms import Alarm, AlarmState from app.models.onions import Eotk, Onion
from app.models.onions import Onion, Eotk
from app.models.tfstate import TerraformState from app.models.tfstate import TerraformState
Model = Type[db.Model] # type: ignore[name-defined] Model = Type[db.Model] # type: ignore[name-defined]

View file

@ -1,10 +1,10 @@
import json import json
import logging import logging
import sys import sys
from typing import Callable, Any from typing import Any, Callable
from app import app from app import app
from app.cli import _SubparserType, BaseCliHandler from app.cli import BaseCliHandler, _SubparserType
from app.lists import lists from app.lists import lists
from app.models.base import Pool from app.models.base import Pool

View file

@ -1,6 +1,6 @@
from flask_bootstrap import Bootstrap5
from flask_migrate import Migrate from flask_migrate import Migrate
from flask_sqlalchemy import SQLAlchemy from flask_sqlalchemy import SQLAlchemy
from flask_bootstrap import Bootstrap5
from sqlalchemy import MetaData from sqlalchemy import MetaData
convention = { convention = {

View file

@ -1,4 +1,4 @@
from typing import Dict, Callable, Any from typing import Any, Callable, Dict
from app.lists.bc2 import mirror_sites from app.lists.bc2 import mirror_sites
from app.lists.bridgelines import bridgelines from app.lists.bridgelines import bridgelines

View file

@ -1,4 +1,5 @@
from typing import List, Optional, TypedDict from typing import List, Optional, TypedDict
from sqlalchemy.orm import selectinload from sqlalchemy.orm import selectinload
from app.models.base import Pool from app.models.base import Pool

View file

@ -1,4 +1,5 @@
from typing import List, Dict, Optional, TypedDict from typing import Dict, List, Optional, TypedDict
from sqlalchemy.orm import selectinload from sqlalchemy.orm import selectinload
from app.models.base import Pool from app.models.base import Pool

View file

@ -7,6 +7,7 @@ from sqlalchemy.orm import Mapped, mapped_column
from app.brm.brn import BRN from app.brm.brn import BRN
from app.extensions import db from app.extensions import db
from app.models.types import AwareDateTime
class AbstractConfiguration(db.Model): # type: ignore class AbstractConfiguration(db.Model): # type: ignore
@ -14,9 +15,9 @@ class AbstractConfiguration(db.Model): # type: ignore
id: Mapped[int] = mapped_column(db.Integer, primary_key=True) id: Mapped[int] = mapped_column(db.Integer, primary_key=True)
description: Mapped[str] description: Mapped[str]
added: Mapped[datetime] = mapped_column(db.DateTime(timezone=True)) added: Mapped[datetime] = mapped_column(AwareDateTime())
updated: Mapped[datetime] = mapped_column(db.DateTime(timezone=True)) updated: Mapped[datetime] = mapped_column(AwareDateTime())
destroyed: Mapped[Optional[datetime]] = mapped_column(db.DateTime(timezone=True), nullable=True) destroyed: Mapped[Optional[datetime]] = mapped_column(AwareDateTime(), nullable=True)
@property @property
@abstractmethod @abstractmethod
@ -43,7 +44,7 @@ class Deprecation(db.Model): # type: ignore[name-defined,misc]
id: Mapped[int] = mapped_column(db.Integer, primary_key=True) id: Mapped[int] = mapped_column(db.Integer, primary_key=True)
resource_type: Mapped[str] resource_type: Mapped[str]
resource_id: Mapped[int] resource_id: Mapped[int]
deprecated_at: Mapped[datetime] = mapped_column(db.DateTime(timezone=True)) deprecated_at: Mapped[datetime] = mapped_column(AwareDateTime())
meta: Mapped[Optional[Dict[str, Any]]] = mapped_column(db.JSON()) meta: Mapped[Optional[Dict[str, Any]]] = mapped_column(db.JSON())
reason: Mapped[str] reason: Mapped[str]
@ -58,11 +59,11 @@ class AbstractResource(db.Model): # type: ignore
__abstract__ = True __abstract__ = True
id: Mapped[int] = mapped_column(db.Integer, primary_key=True) id: Mapped[int] = mapped_column(db.Integer, primary_key=True)
added: Mapped[datetime] = mapped_column(db.DateTime(timezone=True)) added: Mapped[datetime] = mapped_column(AwareDateTime())
updated: Mapped[datetime] = mapped_column(db.DateTime(timezone=True)) updated: Mapped[datetime] = mapped_column(AwareDateTime())
deprecated: Mapped[Optional[datetime]] = mapped_column(db.DateTime(timezone=True), nullable=True) deprecated: Mapped[Optional[datetime]] = mapped_column(AwareDateTime(), nullable=True)
deprecation_reason: Mapped[Optional[str]] deprecation_reason: Mapped[Optional[str]]
destroyed: Mapped[Optional[datetime]] = mapped_column(db.DateTime(timezone=True), nullable=True) destroyed: Mapped[Optional[datetime]] = mapped_column(AwareDateTime(), nullable=True)
def __init__(self, *, def __init__(self, *,
id: Optional[int] = None, id: Optional[int] = None,

View file

@ -7,6 +7,7 @@ from sqlalchemy.orm import Mapped, mapped_column
from app.brm.brn import BRN from app.brm.brn import BRN
from app.extensions import db from app.extensions import db
from app.models import AbstractConfiguration from app.models import AbstractConfiguration
from app.models.types import AwareDateTime
class Activity(db.Model): # type: ignore class Activity(db.Model): # type: ignore
@ -14,7 +15,7 @@ class Activity(db.Model): # type: ignore
group_id: Mapped[Optional[int]] group_id: Mapped[Optional[int]]
activity_type: Mapped[str] activity_type: Mapped[str]
text: Mapped[str] text: Mapped[str]
added: Mapped[datetime] = mapped_column(db.DateTime(timezone=True)) added: Mapped[datetime] = mapped_column(AwareDateTime())
def __init__(self, *, def __init__(self, *,
id: Optional[int] = None, id: Optional[int] = None,

View file

@ -6,6 +6,7 @@ from sqlalchemy.orm import Mapped, mapped_column
from app.extensions import db from app.extensions import db
from app.models.activity import Activity from app.models.activity import Activity
from app.models.types import AwareDateTime
class AlarmState(enum.Enum): class AlarmState(enum.Enum):
@ -30,8 +31,8 @@ class Alarm(db.Model): # type: ignore
target: Mapped[str] target: Mapped[str]
aspect: Mapped[str] aspect: Mapped[str]
alarm_state: Mapped[AlarmState] = mapped_column(default=AlarmState.UNKNOWN) alarm_state: Mapped[AlarmState] = mapped_column(default=AlarmState.UNKNOWN)
state_changed: Mapped[datetime] = mapped_column(db.DateTime(timezone=True)) state_changed: Mapped[datetime] = mapped_column(AwareDateTime())
last_updated: Mapped[datetime] = mapped_column(db.DateTime(timezone=True)) last_updated: Mapped[datetime] = mapped_column(AwareDateTime())
text: Mapped[str] text: Mapped[str]
@classmethod @classmethod

View file

@ -7,6 +7,7 @@ from sqlalchemy.orm import Mapped, mapped_column
from app.brm.brn import BRN from app.brm.brn import BRN
from app.extensions import db from app.extensions import db
from app.models import AbstractConfiguration, AbstractResource from app.models import AbstractConfiguration, AbstractResource
from app.models.types import AwareDateTime
class AutomationState(enum.Enum): class AutomationState(enum.Enum):
@ -19,8 +20,8 @@ class Automation(AbstractConfiguration):
short_name: Mapped[str] short_name: Mapped[str]
state: Mapped[AutomationState] = mapped_column(default=AutomationState.IDLE) state: Mapped[AutomationState] = mapped_column(default=AutomationState.IDLE)
enabled: Mapped[bool] enabled: Mapped[bool]
last_run: Mapped[Optional[datetime]] = mapped_column(db.DateTime(timezone=True)) last_run: Mapped[Optional[datetime]] = mapped_column(AwareDateTime())
next_run: Mapped[Optional[datetime]] = mapped_column(db.DateTime(timezone=True)) next_run: Mapped[Optional[datetime]] = mapped_column(AwareDateTime())
next_is_full: Mapped[bool] next_is_full: Mapped[bool]
logs = db.relationship("AutomationLogs", back_populates="automation") logs = db.relationship("AutomationLogs", back_populates="automation")

View file

@ -8,6 +8,7 @@ from app.brm.brn import BRN
from app.extensions import db from app.extensions import db
from app.models import AbstractConfiguration, AbstractResource from app.models import AbstractConfiguration, AbstractResource
from app.models.base import Pool from app.models.base import Pool
from app.models.types import AwareDateTime
class ProviderAllocation(enum.Enum): class ProviderAllocation(enum.Enum):
@ -54,7 +55,7 @@ class BridgeConf(AbstractConfiguration):
class Bridge(AbstractResource): class Bridge(AbstractResource):
conf_id: Mapped[int] = mapped_column(db.ForeignKey("bridge_conf.id")) conf_id: Mapped[int] = mapped_column(db.ForeignKey("bridge_conf.id"))
cloud_account_id: Mapped[int] = mapped_column(db.ForeignKey("cloud_account.id")) cloud_account_id: Mapped[int] = mapped_column(db.ForeignKey("cloud_account.id"))
terraform_updated: Mapped[Optional[datetime]] = mapped_column(db.DateTime(timezone=True), nullable=True) terraform_updated: Mapped[Optional[datetime]] = mapped_column(AwareDateTime(), nullable=True)
nickname: Mapped[Optional[str]] nickname: Mapped[Optional[str]]
fingerprint: Mapped[Optional[str]] fingerprint: Mapped[Optional[str]]
hashed_fingerprint: Mapped[Optional[str]] hashed_fingerprint: Mapped[Optional[str]]

View file

@ -1,5 +1,5 @@
import enum import enum
from typing import Any, Dict, List, TYPE_CHECKING from typing import TYPE_CHECKING, Any, Dict, List
from sqlalchemy.orm import Mapped, mapped_column, relationship from sqlalchemy.orm import Mapped, mapped_column, relationship
@ -8,7 +8,6 @@ from app.extensions import db
from app.models import AbstractConfiguration from app.models import AbstractConfiguration
from app.models.mirrors import StaticOrigin from app.models.mirrors import StaticOrigin
if TYPE_CHECKING: if TYPE_CHECKING:
from app.models.bridges import Bridge from app.models.bridges import Bridge

View file

@ -16,6 +16,7 @@ from app.extensions import db
from app.models import AbstractConfiguration, AbstractResource, Deprecation from app.models import AbstractConfiguration, AbstractResource, Deprecation
from app.models.base import Group, Pool from app.models.base import Group, Pool
from app.models.onions import Onion from app.models.onions import Onion
from app.models.types import AwareDateTime
country_origin = db.Table( country_origin = db.Table(
'country_origin', 'country_origin',
@ -275,7 +276,7 @@ class Proxy(AbstractResource):
provider: Mapped[str] = mapped_column(db.String(20), nullable=False) provider: Mapped[str] = mapped_column(db.String(20), nullable=False)
psg: Mapped[Optional[int]] = mapped_column(db.Integer, nullable=True) psg: Mapped[Optional[int]] = mapped_column(db.Integer, nullable=True)
slug: Mapped[Optional[str]] = mapped_column(db.String(20), nullable=True) slug: Mapped[Optional[str]] = mapped_column(db.String(20), nullable=True)
terraform_updated: Mapped[Optional[datetime]] = mapped_column(db.DateTime(timezone=True), nullable=True) terraform_updated: Mapped[Optional[datetime]] = mapped_column(AwareDateTime(), nullable=True)
url: Mapped[Optional[str]] = mapped_column(db.String(255), nullable=True) url: Mapped[Optional[str]] = mapped_column(db.String(255), nullable=True)
origin: Mapped[Origin] = relationship("Origin", back_populates="proxies") origin: Mapped[Origin] = relationship("Origin", back_populates="proxies")

View file

@ -7,6 +7,7 @@ from app.brm.brn import BRN
from app.extensions import db from app.extensions import db
from app.models import AbstractConfiguration, AbstractResource from app.models import AbstractConfiguration, AbstractResource
from app.models.base import Group from app.models.base import Group
from app.models.types import AwareDateTime
from app.util.onion import onion_hostname from app.util.onion import onion_hostname
@ -36,7 +37,7 @@ class Onion(AbstractConfiguration):
group_id: Mapped[int] = mapped_column(db.ForeignKey("group.id")) group_id: Mapped[int] = mapped_column(db.ForeignKey("group.id"))
domain_name: Mapped[str] domain_name: Mapped[str]
cert_expiry: Mapped[datetime] = mapped_column(db.DateTime(timezone=True)) cert_expiry: Mapped[datetime] = mapped_column(AwareDateTime())
cert_sans: Mapped[str] cert_sans: Mapped[str]
onion_public_key: Mapped[bytes] onion_public_key: Mapped[bytes]
onion_private_key: Mapped[bytes] onion_private_key: Mapped[bytes]

21
app/models/types.py Normal file
View file

@ -0,0 +1,21 @@
from datetime import timezone
from sqlalchemy import DateTime, TypeDecorator
class AwareDateTime(TypeDecorator):
impl = DateTime(timezone=True)
cache_ok = True
def process_bind_param(self, value, dialect):
# Ensure the value is aware. If it's naive, assume UTC.
if value is not None and value.tzinfo is None:
value = value.replace(tzinfo=timezone.utc)
return value
def process_result_value(self, value, dialect):
# Ensure the value is aware. If it's naive, assume UTC.
if value is not None and value.tzinfo is None:
value = value.replace(tzinfo=timezone.utc)
return value

View file

@ -1,6 +1,7 @@
from typing import Optional from typing import Optional
from flask import render_template, Response, flash, redirect, url_for, Blueprint from flask import (Blueprint, Response, flash, redirect, render_template,
url_for)
from flask.typing import ResponseReturnValue from flask.typing import ResponseReturnValue
from app.extensions import db from app.extensions import db

View file

@ -1,9 +1,10 @@
from typing import List, Union, Optional, Dict, Type from typing import Dict, List, Optional, Type, Union
from flask import render_template, url_for, redirect, Blueprint from flask import Blueprint, redirect, render_template, url_for
from flask.typing import ResponseReturnValue from flask.typing import ResponseReturnValue
from flask_wtf import FlaskForm from flask_wtf import FlaskForm
from wtforms import SelectField, StringField, SubmitField, IntegerField, BooleanField, Form, FormField from wtforms import (BooleanField, Form, FormField, IntegerField, SelectField,
StringField, SubmitField)
from wtforms.validators import InputRequired from wtforms.validators import InputRequired
from app.extensions import db from app.extensions import db

View file

@ -1,4 +1,4 @@
from flask import current_app, render_template, Blueprint, Response from flask import Blueprint, Response, current_app, render_template
from flask.typing import ResponseReturnValue from flask.typing import ResponseReturnValue
from sqlalchemy import desc from sqlalchemy import desc

View file

@ -1,5 +1,5 @@
from flask_wtf import FlaskForm from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField, SelectField from wtforms import SelectField, StringField, SubmitField
class EditMirrorForm(FlaskForm): # type: ignore class EditMirrorForm(FlaskForm): # type: ignore

View file

@ -1,6 +1,6 @@
from typing import Optional from typing import Optional
from flask import redirect, Blueprint from flask import Blueprint, redirect
from flask.typing import ResponseReturnValue from flask.typing import ResponseReturnValue
from app.models.onions import Onion from app.models.onions import Onion

View file

@ -2,15 +2,16 @@ import logging
import secrets import secrets
from datetime import datetime, timezone from datetime import datetime, timezone
from flask import render_template, url_for, flash, redirect, Response, Blueprint import sqlalchemy
from flask import (Blueprint, Response, flash, redirect, render_template,
url_for)
from flask.typing import ResponseReturnValue from flask.typing import ResponseReturnValue
from flask_wtf import FlaskForm from flask_wtf import FlaskForm
import sqlalchemy from wtforms import SelectField, StringField, SubmitField
from wtforms import StringField, SubmitField, SelectField
from wtforms.validators import DataRequired from wtforms.validators import DataRequired
from app.extensions import db from app.extensions import db
from app.models.base import Pool, Group from app.models.base import Group, Pool
from app.portal.util import LifecycleForm from app.portal.util import LifecycleForm
bp = Blueprint("pool", __name__) bp = Blueprint("pool", __name__)

View file

@ -1,4 +1,5 @@
from flask import render_template, Response, flash, redirect, url_for, Blueprint from flask import (Blueprint, Response, flash, redirect, render_template,
url_for)
from flask.typing import ResponseReturnValue from flask.typing import ResponseReturnValue
from sqlalchemy import desc from sqlalchemy import desc

View file

@ -1,4 +1,4 @@
from flask import render_template, Blueprint from flask import Blueprint, render_template
from flask.typing import ResponseReturnValue from flask.typing import ResponseReturnValue
from sqlalchemy import desc from sqlalchemy import desc

View file

@ -1,18 +1,20 @@
import logging import logging
from typing import Optional, List, Any from typing import Any, List, Optional
import sqlalchemy.exc import sqlalchemy.exc
from flask import flash, redirect, url_for, render_template, Response, Blueprint, current_app from flask import (Blueprint, Response, current_app, flash, redirect,
render_template, url_for)
from flask.typing import ResponseReturnValue from flask.typing import ResponseReturnValue
from flask_wtf import FlaskForm from flask_wtf import FlaskForm
from sqlalchemy import exc from sqlalchemy import exc
from wtforms import StringField, SelectField, SubmitField, BooleanField, FileField from wtforms import (BooleanField, FileField, SelectField, StringField,
SubmitField)
from wtforms.validators import DataRequired from wtforms.validators import DataRequired
from app.brm.static import create_static_origin from app.brm.static import create_static_origin
from app.models.base import Group from app.models.base import Group
from app.models.cloud import CloudAccount, CloudProvider from app.models.cloud import CloudAccount, CloudProvider
from app.models.mirrors import StaticOrigin, Origin from app.models.mirrors import Origin, StaticOrigin
from app.portal.util import response_404, view_lifecycle from app.portal.util import response_404, view_lifecycle
bp = Blueprint("static", __name__) bp = Blueprint("static", __name__)

View file

@ -1,4 +1,4 @@
from flask import Response, render_template, flash, redirect, url_for from flask import Response, flash, redirect, render_template, url_for
from flask.typing import ResponseReturnValue from flask.typing import ResponseReturnValue
from flask_wtf import FlaskForm from flask_wtf import FlaskForm
from wtforms import SubmitField from wtforms import SubmitField

View file

@ -1,7 +1,7 @@
import os import os
import stat import stat
from typing import Tuple, Any, Optional from typing import Any, Optional, Tuple
from zipfile import ZipFile, ZipInfo, ZIP_DEFLATED from zipfile import ZIP_DEFLATED, ZipFile, ZipInfo
import jinja2 import jinja2

View file

@ -1,4 +1,4 @@
from typing import Tuple, Optional from typing import Optional, Tuple
import boto3 import boto3
from sqlalchemy import func from sqlalchemy import func
@ -6,8 +6,8 @@ from sqlalchemy import func
from app import app from app import app
from app.alarms import get_or_create_alarm from app.alarms import get_or_create_alarm
from app.extensions import db from app.extensions import db
from app.models.base import Group
from app.models.alarms import AlarmState from app.models.alarms import AlarmState
from app.models.base import Group
from app.models.onions import Eotk from app.models.onions import Eotk
from app.terraform import BaseAutomation from app.terraform import BaseAutomation

View file

@ -7,8 +7,8 @@ from app import app
from app.alarms import get_or_create_alarm from app.alarms import get_or_create_alarm
from app.brm.brn import BRN from app.brm.brn import BRN
from app.extensions import db from app.extensions import db
from app.models.mirrors import Proxy
from app.models.alarms import AlarmState from app.models.alarms import AlarmState
from app.models.mirrors import Proxy
from app.terraform import BaseAutomation from app.terraform import BaseAutomation

View file

@ -1,4 +1,4 @@
from typing import Tuple, Optional from typing import Optional, Tuple
import boto3 import boto3
from sqlalchemy import func from sqlalchemy import func
@ -6,8 +6,8 @@ from sqlalchemy import func
from app import app from app import app
from app.alarms import get_or_create_alarm from app.alarms import get_or_create_alarm
from app.extensions import db from app.extensions import db
from app.models.base import Group
from app.models.alarms import AlarmState from app.models.alarms import AlarmState
from app.models.base import Group
from app.models.mirrors import SmartProxy from app.models.mirrors import SmartProxy
from app.terraform import BaseAutomation from app.terraform import BaseAutomation

View file

@ -1,7 +1,8 @@
from flask import current_app from flask import current_app
from github import Github from github import Github
from app.terraform.block.bridge_reachability import BlockBridgeReachabilityAutomation from app.terraform.block.bridge_reachability import \
BlockBridgeReachabilityAutomation
class BlockBridgeGitHubAutomation(BlockBridgeReachabilityAutomation): class BlockBridgeGitHubAutomation(BlockBridgeReachabilityAutomation):

View file

@ -1,7 +1,8 @@
from flask import current_app from flask import current_app
from gitlab import Gitlab from gitlab import Gitlab
from app.terraform.block.bridge_reachability import BlockBridgeReachabilityAutomation from app.terraform.block.bridge_reachability import \
BlockBridgeReachabilityAutomation
class BlockBridgeGitlabAutomation(BlockBridgeReachabilityAutomation): class BlockBridgeGitlabAutomation(BlockBridgeReachabilityAutomation):

View file

@ -2,7 +2,7 @@ import json
import logging import logging
from io import BytesIO from io import BytesIO
from typing import Any, Optional from typing import Any, Optional
from zipfile import ZipFile, BadZipFile from zipfile import BadZipFile, ZipFile
import lxml # nosec: B410 import lxml # nosec: B410
import requests import requests

View file

@ -1,7 +1,7 @@
from collections.abc import Mapping, Sequence
import json import json
import os import os
from typing import List, Any from collections.abc import Mapping, Sequence
from typing import Any, List
from app import app from app import app
from app.lists import lists from app.lists import lists

View file

@ -1,9 +1,9 @@
import datetime
import os.path import os.path
import sys import sys
from abc import abstractmethod from abc import abstractmethod
import datetime
from collections import defaultdict from collections import defaultdict
from typing import Optional, Any, List, Dict from typing import Any, Dict, List, Optional
from flask import current_app from flask import current_app
from sqlalchemy import text from sqlalchemy import text
@ -11,7 +11,7 @@ from sqlalchemy import text
from app import app from app import app
from app.extensions import db from app.extensions import db
from app.models.base import Group from app.models.base import Group
from app.models.mirrors import Proxy, Origin, SmartProxy from app.models.mirrors import Origin, Proxy, SmartProxy
from app.terraform.terraform import TerraformAutomation from app.terraform.terraform import TerraformAutomation

View file

@ -1,4 +1,4 @@
from typing import Optional, Any from typing import Any, Optional
from app.extensions import db from app.extensions import db
from app.models.mirrors import Proxy from app.models.mirrors import Proxy

View file

@ -235,6 +235,7 @@ class ProxyMetaAutomation(BaseAutomation):
""" """
pools = Pool.query.all() pools = Pool.query.all()
for pool in pools: for pool in pools:
logging.debug(pool.added < datetime.now(tz=timezone.utc))
if pool.id == -1: if pool.id == -1:
continue # Skip hotspare pool continue # Skip hotspare pool
logging.debug("Missing proxy check for %s", pool.pool_name) logging.debug("Missing proxy check for %s", pool.pool_name)

View file

@ -1,6 +1,6 @@
import json import json
from flask import Blueprint, request, Response from flask import Blueprint, Response, request
from flask.typing import ResponseReturnValue from flask.typing import ResponseReturnValue
from app.extensions import db from app.extensions import db

View file

@ -1,6 +1,6 @@
import base64 import base64
import hashlib import hashlib
from typing import Tuple, Optional, List, Dict from typing import Dict, List, Optional, Tuple
def onion_hostname(onion_public_key: bytes) -> str: def onion_hostname(onion_public_key: bytes) -> str:

View file

@ -1,12 +1,12 @@
import ssl import ssl
from datetime import datetime, timezone from datetime import datetime, timezone
from typing import Optional, Tuple, List, Dict, TYPE_CHECKING from typing import TYPE_CHECKING, Dict, List, Optional, Tuple
from cryptography import x509 from cryptography import x509
from cryptography.hazmat._oid import ExtensionOID from cryptography.hazmat._oid import ExtensionOID
from cryptography.hazmat.backends import default_backend from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa, padding from cryptography.hazmat.primitives.asymmetric import padding, rsa
from cryptography.hazmat.primitives.asymmetric.padding import PKCS1v15 from cryptography.hazmat.primitives.asymmetric.padding import PKCS1v15
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicKey from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicKey

View file

@ -0,0 +1,85 @@
"""switch to timezone aware
Revision ID: cb3d6f0cdb86
Revises: 54b31e87fe33
Create Date: 2024-12-06 17:34:51.630311
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'cb3d6f0cdb86'
down_revision = '54b31e87fe33'
branch_labels = None
depends_on = None
def upgrade():
def alter_column_to_timezone_aware(table_name, column_name):
with op.batch_alter_table(table_name, schema=None) as batch_op:
batch_op.alter_column(
column_name,
type_=sa.DateTime(timezone=True)
)
# AbstractConfiguration derived tables
configuration_tables = [
"automation",
"cloud_account",
"bridge_conf",
"country",
"group",
"mirror_list",
"onion",
"origin",
"pool",
"static_origin",
"webhook"
]
for t in configuration_tables:
alter_column_to_timezone_aware(t, 'added')
alter_column_to_timezone_aware(t, 'updated')
alter_column_to_timezone_aware(t, 'destroyed')
# AbstractResource derived tables
resource_tables = [
"bridge",
"proxy",
"smart_proxy",
"automation_logs",
"eotk"
]
for t in resource_tables:
alter_column_to_timezone_aware(t, 'added')
alter_column_to_timezone_aware(t, 'updated')
alter_column_to_timezone_aware(t, 'deprecated')
alter_column_to_timezone_aware(t, 'destroyed')
# Deprecation
alter_column_to_timezone_aware("deprecation", "deprecated_at")
# Activity
alter_column_to_timezone_aware("activity", "added")
# Alarm
alter_column_to_timezone_aware("alarm", "state_changed")
alter_column_to_timezone_aware("alarm", "last_updated")
# Bridge terraform_updated
alter_column_to_timezone_aware("bridge", "terraform_updated")
# Proxy terraform_updated
alter_column_to_timezone_aware("proxy", "terraform_updated")
# Automation last_run, next_run
alter_column_to_timezone_aware("automation", "last_run")
alter_column_to_timezone_aware("automation", "next_run")
# Onion cert_expiry
alter_column_to_timezone_aware("onion", "cert_expiry")
def downgrade():
pass