diff options
| -rw-r--r-- | alembic.ini | 149 | ||||
| -rw-r--r-- | conf/__init__.py | 8 | ||||
| -rw-r--r-- | conf/local.py | 3 | ||||
| -rw-r--r-- | db/__init__.py | 0 | ||||
| -rw-r--r-- | db/migrations/README | 1 | ||||
| -rw-r--r-- | db/migrations/env.py | 82 | ||||
| -rw-r--r-- | db/migrations/script.py.mako | 29 | ||||
| -rw-r--r-- | db/migrations/versions/202607012255-40e20fbf63c9_.py | 35 | ||||
| -rw-r--r-- | db/migrations/versions/202607012255-6ebaa6ab0f48_.py | 39 | ||||
| -rw-r--r-- | db/models.py | 13 | ||||
| -rw-r--r-- | db/session.py | 21 | ||||
| -rw-r--r-- | proxy/endpoints.py | 2 | ||||
| -rw-r--r-- | proxy/queue.py (renamed from ttun_server/proxy_queue.py) | 0 | ||||
| -rw-r--r-- | proxy/websockets.py | 2 | ||||
| -rw-r--r-- | pyproject.toml | 3 | ||||
| -rw-r--r-- | ttun_server/websockets.py | 2 | ||||
| -rw-r--r-- | utils/__init__.py | 0 | ||||
| -rw-r--r-- | utils/json.py | 16 | ||||
| -rw-r--r-- | uv.lock | 121 |
19 files changed, 523 insertions, 3 deletions
diff --git a/alembic.ini b/alembic.ini new file mode 100644 index 0000000..67f7bd4 --- /dev/null +++ b/alembic.ini | |||
| @@ -0,0 +1,149 @@ | |||
| 1 | # A generic, single database configuration. | ||
| 2 | |||
| 3 | [alembic] | ||
| 4 | # path to migration scripts. | ||
| 5 | # this is typically a path given in POSIX (e.g. forward slashes) | ||
| 6 | # format, relative to the token %(here)s which refers to the location of this | ||
| 7 | # ini file | ||
| 8 | script_location = %(here)s/db/migrations | ||
| 9 | |||
| 10 | # template used to generate migration file names; The default value is %%(rev)s_%%(slug)s | ||
| 11 | # Uncomment the line below if you want the files to be prepended with date and time | ||
| 12 | # see https://alembic.sqlalchemy.org/en/latest/tutorial.html#editing-the-ini-file | ||
| 13 | # for all available tokens | ||
| 14 | file_template = %%(year)d%%(month).2d%%(day).2d%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s | ||
| 15 | # Or organize into date-based subdirectories (requires recursive_version_locations = true) | ||
| 16 | # file_template = %%(year)d/%%(month).2d/%%(day).2d_%%(hour).2d%%(minute).2d_%%(second).2d_%%(rev)s_%%(slug)s | ||
| 17 | |||
| 18 | # sys.path path, will be prepended to sys.path if present. | ||
| 19 | # defaults to the current working directory. for multiple paths, the path separator | ||
| 20 | # is defined by "path_separator" below. | ||
| 21 | prepend_sys_path = . | ||
| 22 | |||
| 23 | |||
| 24 | # timezone to use when rendering the date within the migration file | ||
| 25 | # as well as the filename. | ||
| 26 | # If specified, requires the tzdata library which can be installed by adding | ||
| 27 | # `alembic[tz]` to the pip requirements. | ||
| 28 | # string value is passed to ZoneInfo() | ||
| 29 | # leave blank for localtime | ||
| 30 | # timezone = | ||
| 31 | |||
| 32 | # max length of characters to apply to the "slug" field | ||
| 33 | # truncate_slug_length = 40 | ||
| 34 | |||
| 35 | # set to 'true' to run the environment during | ||
| 36 | # the 'revision' command, regardless of autogenerate | ||
| 37 | # revision_environment = false | ||
| 38 | |||
| 39 | # set to 'true' to allow .pyc and .pyo files without | ||
| 40 | # a source .py file to be detected as revisions in the | ||
| 41 | # versions/ directory | ||
| 42 | # sourceless = false | ||
| 43 | |||
| 44 | # version location specification; This defaults | ||
| 45 | # to <script_location>/versions. When using multiple version | ||
| 46 | # directories, initial revisions must be specified with --version-path. | ||
| 47 | # The path separator used here should be the separator specified by "path_separator" | ||
| 48 | # below. | ||
| 49 | # version_locations = %(here)s/bar:%(here)s/bat:%(here)s/alembic/versions | ||
| 50 | |||
| 51 | # path_separator; This indicates what character is used to split lists of file | ||
| 52 | # paths, including version_locations and prepend_sys_path within configparser | ||
| 53 | # files such as alembic.ini. | ||
| 54 | # The default rendered in new alembic.ini files is "os", which uses os.pathsep | ||
| 55 | # to provide os-dependent path splitting. | ||
| 56 | # | ||
| 57 | # Note that in order to support legacy alembic.ini files, this default does NOT | ||
| 58 | # take place if path_separator is not present in alembic.ini. If this | ||
| 59 | # option is omitted entirely, fallback logic is as follows: | ||
| 60 | # | ||
| 61 | # 1. Parsing of the version_locations option falls back to using the legacy | ||
| 62 | # "version_path_separator" key, which if absent then falls back to the legacy | ||
| 63 | # behavior of splitting on spaces and/or commas. | ||
| 64 | # 2. Parsing of the prepend_sys_path option falls back to the legacy | ||
| 65 | # behavior of splitting on spaces, commas, or colons. | ||
| 66 | # | ||
| 67 | # Valid values for path_separator are: | ||
| 68 | # | ||
| 69 | # path_separator = : | ||
| 70 | # path_separator = ; | ||
| 71 | # path_separator = space | ||
| 72 | # path_separator = newline | ||
| 73 | # | ||
| 74 | # Use os.pathsep. Default configuration used for new projects. | ||
| 75 | path_separator = os | ||
| 76 | |||
| 77 | # set to 'true' to search source files recursively | ||
| 78 | # in each "version_locations" directory | ||
| 79 | # new in Alembic version 1.10 | ||
| 80 | # recursive_version_locations = false | ||
| 81 | |||
| 82 | # the output encoding used when revision files | ||
| 83 | # are written from script.py.mako | ||
| 84 | # output_encoding = utf-8 | ||
| 85 | |||
| 86 | # database URL. This is consumed by the user-maintained env.py script only. | ||
| 87 | # other means of configuring database URLs may be customized within the env.py | ||
| 88 | # file. | ||
| 89 | sqlalchemy.url = driver://user:pass@localhost/dbname | ||
| 90 | |||
| 91 | |||
| 92 | [post_write_hooks] | ||
| 93 | # post_write_hooks defines scripts or Python functions that are run | ||
| 94 | # on newly generated revision scripts. See the documentation for further | ||
| 95 | # detail and examples | ||
| 96 | |||
| 97 | # format using "black" - use the console_scripts runner, against the "black" entrypoint | ||
| 98 | # hooks = black | ||
| 99 | # black.type = console_scripts | ||
| 100 | # black.entrypoint = black | ||
| 101 | # black.options = -l 79 REVISION_SCRIPT_FILENAME | ||
| 102 | |||
| 103 | # lint with attempts to fix using "ruff" - use the module runner, against the "ruff" module | ||
| 104 | # hooks = ruff | ||
| 105 | # ruff.type = module | ||
| 106 | # ruff.module = ruff | ||
| 107 | # ruff.options = check --fix REVISION_SCRIPT_FILENAME | ||
| 108 | |||
| 109 | # Alternatively, use the exec runner to execute a binary found on your PATH | ||
| 110 | # hooks = ruff | ||
| 111 | # ruff.type = exec | ||
| 112 | # ruff.executable = ruff | ||
| 113 | # ruff.options = check --fix REVISION_SCRIPT_FILENAME | ||
| 114 | |||
| 115 | # Logging configuration. This is also consumed by the user-maintained | ||
| 116 | # env.py script only. | ||
| 117 | [loggers] | ||
| 118 | keys = root,sqlalchemy,alembic | ||
| 119 | |||
| 120 | [handlers] | ||
| 121 | keys = console | ||
| 122 | |||
| 123 | [formatters] | ||
| 124 | keys = generic | ||
| 125 | |||
| 126 | [logger_root] | ||
| 127 | level = WARNING | ||
| 128 | handlers = console | ||
| 129 | qualname = | ||
| 130 | |||
| 131 | [logger_sqlalchemy] | ||
| 132 | level = WARNING | ||
| 133 | handlers = | ||
| 134 | qualname = sqlalchemy.engine | ||
| 135 | |||
| 136 | [logger_alembic] | ||
| 137 | level = INFO | ||
| 138 | handlers = | ||
| 139 | qualname = alembic | ||
| 140 | |||
| 141 | [handler_console] | ||
| 142 | class = StreamHandler | ||
| 143 | args = (sys.stderr,) | ||
| 144 | level = NOTSET | ||
| 145 | formatter = generic | ||
| 146 | |||
| 147 | [formatter_generic] | ||
| 148 | format = %(levelname)-5.5s [%(name)s] %(message)s | ||
| 149 | datefmt = %H:%M:%S | ||
diff --git a/conf/__init__.py b/conf/__init__.py new file mode 100644 index 0000000..c08dcee --- /dev/null +++ b/conf/__init__.py | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | import os | ||
| 2 | |||
| 3 | try: | ||
| 4 | from .local import * | ||
| 5 | except ImportError: | ||
| 6 | pass | ||
| 7 | |||
| 8 | DATABASE_URL = os.environ.get('DATABASE_URL') | ||
diff --git a/conf/local.py b/conf/local.py new file mode 100644 index 0000000..608e0e5 --- /dev/null +++ b/conf/local.py | |||
| @@ -0,0 +1,3 @@ | |||
| 1 | import os | ||
| 2 | |||
| 3 | os.environ.setdefault('DATABASE_URL', 'postgresql:///ttun') | ||
diff --git a/db/__init__.py b/db/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/db/__init__.py | |||
diff --git a/db/migrations/README b/db/migrations/README new file mode 100644 index 0000000..98e4f9c --- /dev/null +++ b/db/migrations/README | |||
| @@ -0,0 +1 @@ | |||
| Generic single-database configuration. \ No newline at end of file | |||
diff --git a/db/migrations/env.py b/db/migrations/env.py new file mode 100644 index 0000000..f1b3156 --- /dev/null +++ b/db/migrations/env.py | |||
| @@ -0,0 +1,82 @@ | |||
| 1 | from logging.config import fileConfig | ||
| 2 | |||
| 3 | from sqlalchemy import engine_from_config | ||
| 4 | from sqlalchemy import pool | ||
| 5 | |||
| 6 | from alembic import context | ||
| 7 | |||
| 8 | from conf import DATABASE_URL | ||
| 9 | from db.models import * | ||
| 10 | |||
| 11 | # this is the Alembic Config object, which provides | ||
| 12 | # access to the values within the .ini file in use. | ||
| 13 | config = context.config | ||
| 14 | |||
| 15 | # Interpret the config file for Python logging. | ||
| 16 | # This line sets up loggers basically. | ||
| 17 | if config.config_file_name is not None: | ||
| 18 | fileConfig(config.config_file_name) | ||
| 19 | |||
| 20 | # add your model's MetaData object here | ||
| 21 | # for 'autogenerate' support | ||
| 22 | # from myapp import mymodel | ||
| 23 | # target_metadata = mymodel.Base.metadata | ||
| 24 | target_metadata = SQLModel.metadata | ||
| 25 | |||
| 26 | # other values from the config, defined by the needs of env.py, | ||
| 27 | # can be acquired: | ||
| 28 | # my_important_option = config.get_main_option("my_important_option") | ||
| 29 | # ... etc. | ||
| 30 | config.set_main_option('sqlalchemy.url', DATABASE_URL) | ||
| 31 | |||
| 32 | |||
| 33 | def run_migrations_offline() -> None: | ||
| 34 | """Run migrations in 'offline' mode. | ||
| 35 | |||
| 36 | This configures the context with just a URL | ||
| 37 | and not an Engine, though an Engine is acceptable | ||
| 38 | here as well. By skipping the Engine creation | ||
| 39 | we don't even need a DBAPI to be available. | ||
| 40 | |||
| 41 | Calls to context.execute() here emit the given string to the | ||
| 42 | script output. | ||
| 43 | |||
| 44 | """ | ||
| 45 | url = config.get_main_option("sqlalchemy.url") | ||
| 46 | context.configure( | ||
| 47 | url=url, | ||
| 48 | target_metadata=target_metadata, | ||
| 49 | literal_binds=True, | ||
| 50 | dialect_opts={"paramstyle": "named"}, | ||
| 51 | ) | ||
