mirror of
https://github.com/hay-kot/mealie.git
synced 2025-08-22 06:23:34 -07:00
Add alembic
This commit is contained in:
parent
7559c66127
commit
3022cc20ec
19 changed files with 2712 additions and 2227 deletions
89
alembic.ini
Normal file
89
alembic.ini
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
# A generic, single database configuration.
|
||||||
|
|
||||||
|
[alembic]
|
||||||
|
# path to migration scripts
|
||||||
|
script_location = alembic
|
||||||
|
|
||||||
|
# template used to generate migration files
|
||||||
|
# file_template = %%(rev)s_%%(slug)s
|
||||||
|
|
||||||
|
# sys.path path, will be prepended to sys.path if present.
|
||||||
|
# defaults to the current working directory.
|
||||||
|
prepend_sys_path = .
|
||||||
|
|
||||||
|
# timezone to use when rendering the date
|
||||||
|
# within the migration file as well as the filename.
|
||||||
|
# string value is passed to dateutil.tz.gettz()
|
||||||
|
# leave blank for localtime
|
||||||
|
# timezone =
|
||||||
|
|
||||||
|
# max length of characters to apply to the
|
||||||
|
# "slug" field
|
||||||
|
# truncate_slug_length = 40
|
||||||
|
|
||||||
|
# set to 'true' to run the environment during
|
||||||
|
# the 'revision' command, regardless of autogenerate
|
||||||
|
# revision_environment = false
|
||||||
|
|
||||||
|
# set to 'true' to allow .pyc and .pyo files without
|
||||||
|
# a source .py file to be detected as revisions in the
|
||||||
|
# versions/ directory
|
||||||
|
# sourceless = false
|
||||||
|
|
||||||
|
# version location specification; this defaults
|
||||||
|
# to alembic/versions. When using multiple version
|
||||||
|
# directories, initial revisions must be specified with --version-path
|
||||||
|
# version_locations = %(here)s/bar %(here)s/bat alembic/versions
|
||||||
|
|
||||||
|
# the output encoding used when revision files
|
||||||
|
# are written from script.py.mako
|
||||||
|
# output_encoding = utf-8
|
||||||
|
|
||||||
|
sqlalchemy.url = sqlite:///mealie.db
|
||||||
|
|
||||||
|
|
||||||
|
[post_write_hooks]
|
||||||
|
# post_write_hooks defines scripts or Python functions that are run
|
||||||
|
# on newly generated revision scripts. See the documentation for further
|
||||||
|
# detail and examples
|
||||||
|
|
||||||
|
# format using "black" - use the console_scripts runner, against the "black" entrypoint
|
||||||
|
# hooks=black
|
||||||
|
# black.type=console_scripts
|
||||||
|
# black.entrypoint=black
|
||||||
|
# black.options=-l 79
|
||||||
|
|
||||||
|
# Logging configuration
|
||||||
|
[loggers]
|
||||||
|
keys = root,sqlalchemy,alembic
|
||||||
|
|
||||||
|
[handlers]
|
||||||
|
keys = console
|
||||||
|
|
||||||
|
[formatters]
|
||||||
|
keys = generic
|
||||||
|
|
||||||
|
[logger_root]
|
||||||
|
level = WARN
|
||||||
|
handlers = console
|
||||||
|
qualname =
|
||||||
|
|
||||||
|
[logger_sqlalchemy]
|
||||||
|
level = WARN
|
||||||
|
handlers =
|
||||||
|
qualname = sqlalchemy.engine
|
||||||
|
|
||||||
|
[logger_alembic]
|
||||||
|
level = INFO
|
||||||
|
handlers =
|
||||||
|
qualname = alembic
|
||||||
|
|
||||||
|
[handler_console]
|
||||||
|
class = StreamHandler
|
||||||
|
args = (sys.stderr,)
|
||||||
|
level = NOTSET
|
||||||
|
formatter = generic
|
||||||
|
|
||||||
|
[formatter_generic]
|
||||||
|
format = %(levelname)-5.5s [%(name)s] %(message)s
|
||||||
|
datefmt = %H:%M:%S
|
1
alembic/README
Normal file
1
alembic/README
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Generic single-database configuration.
|
79
alembic/env.py
Normal file
79
alembic/env.py
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
from logging.config import fileConfig
|
||||||
|
|
||||||
|
from sqlalchemy import engine_from_config
|
||||||
|
from sqlalchemy import pool
|
||||||
|
|
||||||
|
from alembic import context
|
||||||
|
|
||||||
|
# this is the Alembic Config object, which provides
|
||||||
|
# access to the values within the .ini file in use.
|
||||||
|
config = context.config
|
||||||
|
|
||||||
|
# Interpret the config file for Python logging.
|
||||||
|
# This line sets up loggers basically.
|
||||||
|
fileConfig(config.config_file_name)
|
||||||
|
|
||||||
|
# add your model's MetaData object here
|
||||||
|
# for 'autogenerate' support
|
||||||
|
# from myapp import mymodel
|
||||||
|
# target_metadata = mymodel.Base.metadata
|
||||||
|
from mealie.db.models.model_base import SqlAlchemyBase as Base
|
||||||
|
import mealie.db.models._all_models
|
||||||
|
target_metadata = Base.metadata
|
||||||
|
|
||||||
|
# other values from the config, defined by the needs of env.py,
|
||||||
|
# can be acquired:
|
||||||
|
# my_important_option = config.get_main_option("my_important_option")
|
||||||
|
# ... etc.
|
||||||
|
|
||||||
|
|
||||||
|
def run_migrations_offline():
|
||||||
|
"""Run migrations in 'offline' mode.
|
||||||
|
|
||||||
|
This configures the context with just a URL
|
||||||
|
and not an Engine, though an Engine is acceptable
|
||||||
|
here as well. By skipping the Engine creation
|
||||||
|
we don't even need a DBAPI to be available.
|
||||||
|
|
||||||
|
Calls to context.execute() here emit the given string to the
|
||||||
|
script output.
|
||||||
|
|
||||||
|
"""
|
||||||
|
url = config.get_main_option("sqlalchemy.url")
|
||||||
|
context.configure(
|
||||||
|
url=url,
|
||||||
|
target_metadata=target_metadata,
|
||||||
|
literal_binds=True,
|
||||||
|
dialect_opts={"paramstyle": "named"},
|
||||||
|
)
|
||||||
|
|
||||||
|
with context.begin_transaction():
|
||||||
|
context.run_migrations()
|
||||||
|
|
||||||
|
|
||||||
|
def run_migrations_online():
|
||||||
|
"""Run migrations in 'online' mode.
|
||||||
|
|
||||||
|
In this scenario we need to create an Engine
|
||||||
|
and associate a connection with the context.
|
||||||
|
|
||||||
|
"""
|
||||||
|
connectable = engine_from_config(
|
||||||
|
config.get_section(config.config_ini_section),
|
||||||
|
prefix="sqlalchemy.",
|
||||||
|
poolclass=pool.NullPool,
|
||||||
|
)
|
||||||
|
|
||||||
|
with connectable.connect() as connection:
|
||||||
|
context.configure(
|
||||||
|
connection=connection, target_metadata=target_metadata
|
||||||
|
)
|
||||||
|
|
||||||
|
with context.begin_transaction():
|
||||||
|
context.run_migrations()
|
||||||
|
|
||||||
|
|
||||||
|
if context.is_offline_mode():
|
||||||
|
run_migrations_offline()
|
||||||
|
else:
|
||||||
|
run_migrations_online()
|
24
alembic/script.py.mako
Normal file
24
alembic/script.py.mako
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
"""${message}
|
||||||
|
|
||||||
|
Revision ID: ${up_revision}
|
||||||
|
Revises: ${down_revision | comma,n}
|
||||||
|
Create Date: ${create_date}
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
${imports if imports else ""}
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = ${repr(up_revision)}
|
||||||
|
down_revision = ${repr(down_revision)}
|
||||||
|
branch_labels = ${repr(branch_labels)}
|
||||||
|
depends_on = ${repr(depends_on)}
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
${upgrades if upgrades else "pass"}
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
${downgrades if downgrades else "pass"}
|
318
alembic/versions/89e8733c36f2_initial_revision.py
Normal file
318
alembic/versions/89e8733c36f2_initial_revision.py
Normal file
|
@ -0,0 +1,318 @@
|
||||||
|
"""Initial revision
|
||||||
|
|
||||||
|
Revision ID: 89e8733c36f2
|
||||||
|
Revises:
|
||||||
|
Create Date: 2021-04-27 23:37:22.500465
|
||||||
|
|
||||||
|
"""
|
||||||
|
from mealie.core.config import settings
|
||||||
|
from mealie.core.security import get_password_hash
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '89e8733c36f2'
|
||||||
|
down_revision = None
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
categories_table = op.create_table('categories',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('name', sa.String(), nullable=False),
|
||||||
|
sa.Column('slug', sa.String(), nullable=False),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
op.create_index(op.f('ix_categories_name'), 'categories', ['name'], unique=False)
|
||||||
|
op.create_index(op.f('ix_categories_slug'), 'categories', ['slug'], unique=True)
|
||||||
|
custom_pages_table = op.create_table('custom_pages',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('position', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('name', sa.String(), nullable=False),
|
||||||
|
sa.Column('slug', sa.String(), nullable=False),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
groups_table = op.create_table('groups',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('name', sa.String(), nullable=False),
|
||||||
|
sa.Column('webhook_enable', sa.Boolean(), nullable=True),
|
||||||
|
sa.Column('webhook_time', sa.String(), nullable=True),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
op.create_index(op.f('ix_groups_name'), 'groups', ['name'], unique=True)
|
||||||
|
recipes_table = op.create_table('recipes',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('name', sa.String(), nullable=False),
|
||||||
|
sa.Column('description', sa.String(), nullable=True),
|
||||||
|
sa.Column('image', sa.String(), nullable=True),
|
||||||
|
sa.Column('totalTime', sa.String(), nullable=True),
|
||||||
|
sa.Column('prepTime', sa.String(), nullable=True),
|
||||||
|
sa.Column('performTime', sa.String(), nullable=True),
|
||||||
|
sa.Column('cookTime', sa.String(), nullable=True),
|
||||||
|
sa.Column('recipeYield', sa.String(), nullable=True),
|
||||||
|
sa.Column('recipeCuisine', sa.String(), nullable=True),
|
||||||
|
sa.Column('slug', sa.String(), nullable=True),
|
||||||
|
sa.Column('dateAdded', sa.Date(), nullable=True),
|
||||||
|
sa.Column('rating', sa.Integer(), nullable=True),
|
||||||
|
sa.Column('orgURL', sa.String(), nullable=True),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
op.create_index(op.f('ix_recipes_slug'), 'recipes', ['slug'], unique=True)
|
||||||
|
sign_ups_table = op.create_table('sign_ups',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('token', sa.String(), nullable=False),
|
||||||
|
sa.Column('name', sa.String(), nullable=True),
|
||||||
|
sa.Column('admin', sa.Boolean(), nullable=True),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
op.create_index(op.f('ix_sign_ups_name'), 'sign_ups', ['name'], unique=False)
|
||||||
|
op.create_index(op.f('ix_sign_ups_token'), 'sign_ups', ['token'], unique=False)
|
||||||
|
site_settings_table = op.create_table('site_settings',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('language', sa.String(), nullable=True),
|
||||||
|
sa.Column('show_recent', sa.Boolean(), nullable=True),
|
||||||
|
sa.Column('cards_per_section', sa.Integer(), nullable=True),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
site_theme_table = op.create_table('site_theme',
|
||||||
|
sa.Column('name', sa.String(), nullable=False),
|
||||||
|
sa.PrimaryKeyConstraint('name')
|
||||||
|
)
|
||||||
|
tags_table = op.create_table('tags',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('name', sa.String(), nullable=False),
|
||||||
|
sa.Column('slug', sa.String(), nullable=False),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
op.create_index(op.f('ix_tags_name'), 'tags', ['name'], unique=False)
|
||||||
|
op.create_index(op.f('ix_tags_slug'), 'tags', ['slug'], unique=True)
|
||||||
|
api_extras_table = op.create_table('api_extras',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('parent_id', sa.Integer(), nullable=True),
|
||||||
|
sa.Column('key_name', sa.String(), nullable=True),
|
||||||
|
sa.Column('value', sa.String(), nullable=True),
|
||||||
|
sa.ForeignKeyConstraint(['parent_id'], ['recipes.id'], ),
|
||||||
|
sa.PrimaryKeyConstraint('id'),
|
||||||
|
sa.UniqueConstraint('key_name')
|
||||||
|
)
|
||||||
|
custom_pages2categories_table = op.create_table('custom_pages2categories',
|
||||||
|
sa.Column('custom_page_id', sa.Integer(), nullable=True),
|
||||||
|
sa.Column('category_slug', sa.String(), nullable=True),
|
||||||
|
sa.ForeignKeyConstraint(['category_slug'], ['categories.slug'], ),
|
||||||
|
sa.ForeignKeyConstraint(['custom_page_id'], ['custom_pages.id'], )
|
||||||
|
)
|
||||||
|
group2categories_table = op.create_table('group2categories',
|
||||||
|
sa.Column('group_id', sa.Integer(), nullable=True),
|
||||||
|
sa.Column('category_slug', sa.String(), nullable=True),
|
||||||
|
sa.ForeignKeyConstraint(['category_slug'], ['categories.slug'], ),
|
||||||
|
sa.ForeignKeyConstraint(['group_id'], ['groups.id'], )
|
||||||
|
)
|
||||||
|
mealplan_table = op.create_table('mealplan',
|
||||||
|
sa.Column('uid', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('startDate', sa.Date(), nullable=True),
|
||||||
|
sa.Column('endDate', sa.Date(), nullable=True),
|
||||||
|
sa.Column('group_id', sa.Integer(), nullable=True),
|
||||||
|
sa.ForeignKeyConstraint(['group_id'], ['groups.id'], ),
|
||||||
|
sa.PrimaryKeyConstraint('uid'),
|
||||||
|
sa.UniqueConstraint('uid')
|
||||||
|
)
|
||||||
|
notes_table = op.create_table('notes',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('parent_id', sa.Integer(), nullable=True),
|
||||||
|
sa.Column('title', sa.String(), nullable=True),
|
||||||
|
sa.Column('text', sa.String(), nullable=True),
|
||||||
|
sa.ForeignKeyConstraint(['parent_id'], ['recipes.id'], ),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
recipe_instructions_table = op.create_table('recipe_instructions',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('parent_id', sa.Integer(), nullable=True),
|
||||||
|
sa.Column('position', sa.Integer(), nullable=True),
|
||||||
|
sa.Column('type', sa.String(), nullable=True),
|
||||||
|
sa.Column('text', sa.String(), nullable=True),
|
||||||
|
sa.ForeignKeyConstraint(['parent_id'], ['recipes.id'], ),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
recipe_nutrition_table = op.create_table('recipe_nutrition',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('parent_id', sa.Integer(), nullable=True),
|
||||||
|
sa.Column('calories', sa.String(), nullable=True),
|
||||||
|
sa.Column('fatContent', sa.String(), nullable=True),
|
||||||
|
sa.Column('fiberContent', sa.String(), nullable=True),
|
||||||
|
sa.Column('proteinContent', sa.String(), nullable=True),
|
||||||
|
sa.Column('sodiumContent', sa.String(), nullable=True),
|
||||||
|
sa.Column('sugarContent', sa.String(), nullable=True),
|
||||||
|
sa.ForeignKeyConstraint(['parent_id'], ['recipes.id'], ),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
recipes2categories_table = op.create_table('recipes2categories',
|
||||||
|
sa.Column('recipe_id', sa.Integer(), nullable=True),
|
||||||
|
sa.Column('category_slug', sa.String(), nullable=True),
|
||||||
|
sa.ForeignKeyConstraint(['category_slug'], ['categories.slug'], ),
|
||||||
|
sa.ForeignKeyConstraint(['recipe_id'], ['recipes.id'], )
|
||||||
|
)
|
||||||
|
recipes2tags_table = op.create_table('recipes2tags',
|
||||||
|
sa.Column('recipe_id', sa.Integer(), nullable=True),
|
||||||
|
sa.Column('tag_slug', sa.String(), nullable=True),
|
||||||
|
sa.ForeignKeyConstraint(['recipe_id'], ['recipes.id'], ),
|
||||||
|
sa.ForeignKeyConstraint(['tag_slug'], ['tags.slug'], )
|
||||||
|
)
|
||||||
|
recipes_ingredients_table = op.create_table('recipes_ingredients',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('position', sa.Integer(), nullable=True),
|
||||||
|
sa.Column('parent_id', sa.Integer(), nullable=True),
|
||||||
|
sa.Column('ingredient', sa.String(), nullable=True),
|
||||||
|
sa.ForeignKeyConstraint(['parent_id'], ['recipes.id'], ),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
site_settings2categories_table = op.create_table('site_settings2categories_table',
|
||||||
|
sa.Column('sidebar_id', sa.Integer(), nullable=True),
|
||||||
|
sa.Column('category_slug', sa.String(), nullable=True),
|
||||||
|
sa.ForeignKeyConstraint(['category_slug'], ['categories.slug'], ),
|
||||||
|
sa.ForeignKeyConstraint(['sidebar_id'], ['site_settings.id'], )
|
||||||
|
)
|
||||||
|
theme_colors_table = op.create_table('theme_colors',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('parent_id', sa.String(), nullable=True),
|
||||||
|
sa.Column('primary', sa.String(), nullable=True),
|
||||||
|
sa.Column('accent', sa.String(), nullable=True),
|
||||||
|
sa.Column('secondary', sa.String(), nullable=True),
|
||||||
|
sa.Column('success', sa.String(), nullable=True),
|
||||||
|
sa.Column('info', sa.String(), nullable=True),
|
||||||
|
sa.Column('warning', sa.String(), nullable=True),
|
||||||
|
sa.Column('error', sa.String(), nullable=True),
|
||||||
|
sa.ForeignKeyConstraint(['parent_id'], ['site_theme.name'], ),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
tools_table = op.create_table('tools',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('parent_id', sa.Integer(), nullable=True),
|
||||||
|
sa.Column('tool', sa.String(), nullable=True),
|
||||||
|
sa.ForeignKeyConstraint(['parent_id'], ['recipes.id'], ),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
users_table = op.create_table('users',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('full_name', sa.String(), nullable=True),
|
||||||
|
sa.Column('email', sa.String(), nullable=True),
|
||||||
|
sa.Column('password', sa.String(), nullable=True),
|
||||||
|
sa.Column('group_id', sa.Integer(), nullable=True),
|
||||||
|
sa.Column('admin', sa.Boolean(), nullable=True),
|
||||||
|
sa.ForeignKeyConstraint(['group_id'], ['groups.id'], ),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
op.create_index(op.f('ix_users_email'), 'users', ['email'], unique=True)
|
||||||
|
op.create_index(op.f('ix_users_full_name'), 'users', ['full_name'], unique=False)
|
||||||
|
webhook_urls_table = op.create_table('webhook_urls',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('url', sa.String(), nullable=True),
|
||||||
|
sa.Column('parent_id', sa.Integer(), nullable=True),
|
||||||
|
sa.ForeignKeyConstraint(['parent_id'], ['groups.id'], ),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
meal_table = op.create_table('meal',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('parent_id', sa.Integer(), nullable=True),
|
||||||
|
sa.Column('slug', sa.String(), nullable=True),
|
||||||
|
sa.Column('name', sa.String(), nullable=True),
|
||||||
|
sa.Column('date', sa.Date(), nullable=True),
|
||||||
|
sa.Column('image', sa.String(), nullable=True),
|
||||||
|
sa.Column('description', sa.String(), nullable=True),
|
||||||
|
sa.ForeignKeyConstraint(['parent_id'], ['mealplan.uid'], ),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
# seed data
|
||||||
|
op.bulk_insert(site_settings_table, [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"language": "en",
|
||||||
|
"firstDayOfWeek": 0,
|
||||||
|
"showRecent": True,
|
||||||
|
}
|
||||||
|
])
|
||||||
|
op.bulk_insert(categories_table, [
|
||||||
|
{"id": 1, "name": "thanksgiving", "slug": "thanksgiving"},
|
||||||
|
{"id": 2, "name": "homechef", "slug": "homechef"},
|
||||||
|
{"id": 3, "name": "potatoes", "slug": "potatoes"},
|
||||||
|
])
|
||||||
|
op.bulk_insert(site_settings2categories_table, [
|
||||||
|
{
|
||||||
|
"sidebar_id": 1,
|
||||||
|
"category_slug", "thanksgiving",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sidebar_id": 1,
|
||||||
|
"category_slug", "homechef",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sidebar_id": 1,
|
||||||
|
"category_slug", "potatoes",
|
||||||
|
},
|
||||||
|
])
|
||||||
|
op.bulk_insert(site_theme_table, [
|
||||||
|
{
|
||||||
|
"name": "default",
|
||||||
|
"colors": {
|
||||||
|
"primary": "#E58325",
|
||||||
|
"accent": "#00457A",
|
||||||
|
"secondary": "#973542",
|
||||||
|
"success": "#5AB1BB",
|
||||||
|
"info": "#4990BA",
|
||||||
|
"warning": "#FF4081",
|
||||||
|
"error": "#EF5350",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
])
|
||||||
|
op.bulk_insert(groups_table, [
|
||||||
|
{
|
||||||
|
"name": settings.DEFAULT_GROUP,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
op.bulk_insert(users_table, [
|
||||||
|
{
|
||||||
|
"full_name": "Change Me",
|
||||||
|
"email": settings.DEFAULT_EMAIL,
|
||||||
|
"password": get_password_hash(settings.DEFAULT_PASSWORD),
|
||||||
|
"group": settings.DEFAULT_GROUP,
|
||||||
|
"admin": True,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
op.drop_table('meal')
|
||||||
|
op.drop_table('webhook_urls')
|
||||||
|
op.drop_index(op.f('ix_users_full_name'), table_name='users')
|
||||||
|
op.drop_index(op.f('ix_users_email'), table_name='users')
|
||||||
|
op.drop_table('users')
|
||||||
|
op.drop_table('tools')
|
||||||
|
op.drop_table('theme_colors')
|
||||||
|
op.drop_table('site_settings2categories')
|
||||||
|
op.drop_table('recipes_ingredients')
|
||||||
|
op.drop_table('recipes2tags')
|
||||||
|
op.drop_table('recipes2categories')
|
||||||
|
op.drop_table('recipe_nutrition')
|
||||||
|
op.drop_table('recipe_instructions')
|
||||||
|
op.drop_table('notes')
|
||||||
|
op.drop_table('mealplan')
|
||||||
|
op.drop_table('group2categories')
|
||||||
|
op.drop_table('custom_pages2categories')
|
||||||
|
op.drop_table('api_extras')
|
||||||
|
op.drop_index(op.f('ix_tags_slug'), table_name='tags')
|
||||||
|
op.drop_index(op.f('ix_tags_name'), table_name='tags')
|
||||||
|
op.drop_table('tags')
|
||||||
|
op.drop_table('site_theme')
|
||||||
|
op.drop_table('site_settings')
|
||||||
|
op.drop_index(op.f('ix_sign_ups_token'), table_name='sign_ups')
|
||||||
|
op.drop_index(op.f('ix_sign_ups_name'), table_name='sign_ups')
|
||||||
|
op.drop_table('sign_ups')
|
||||||
|
op.drop_index(op.f('ix_recipes_slug'), table_name='recipes')
|
||||||
|
op.drop_table('recipes')
|
||||||
|
op.drop_index(op.f('ix_groups_name'), table_name='groups')
|
||||||
|
op.drop_table('groups')
|
||||||
|
op.drop_table('custom_pages')
|
||||||
|
op.drop_index(op.f('ix_categories_slug'), table_name='categories')
|
||||||
|
op.drop_index(op.f('ix_categories_name'), table_name='categories')
|
||||||
|
op.drop_table('categories')
|
|
@ -123,7 +123,7 @@ The frontend static files are generated with `npm run build`. This is done durin
|
||||||
### Backend API
|
### Backend API
|
||||||
The backend API is build with Python, FastAPI, and SQLite and requires Python 3.9, and Poetry. Once the requirements are installed, in the project directory you can run the command `poetry install` to create a python virtual environment and install the python dependencies.
|
The backend API is build with Python, FastAPI, and SQLite and requires Python 3.9, and Poetry. Once the requirements are installed, in the project directory you can run the command `poetry install` to create a python virtual environment and install the python dependencies.
|
||||||
|
|
||||||
Once the dependencies are installed you should be ready to run the server. To initialize that database you need to first run `python mealie/db/init_db.py`. Then to start The web server, you run the command `uvicorn mealie.app:app --host 0.0.0.0 --port 9000`
|
Once the dependencies are installed you should be ready to run the server. To initialize that database you need to first run `alembic upgrade head`. Then to start The web server, you run the command `uvicorn mealie.app:app --host 0.0.0.0 --port 9000`
|
||||||
|
|
||||||
|
|
||||||
### Proxy Server
|
### Proxy Server
|
||||||
|
|
2
makefile
2
makefile
|
@ -53,7 +53,7 @@ setup: ## Setup Development Instance
|
||||||
cd ..
|
cd ..
|
||||||
|
|
||||||
backend: ## Start Mealie Backend Development Server
|
backend: ## Start Mealie Backend Development Server
|
||||||
poetry run python mealie/db/init_db.py && \
|
poetry run alembic upgrade head && \
|
||||||
poetry run python mealie/services/image/minify.py && \
|
poetry run python mealie/services/image/minify.py && \
|
||||||
poetry run python mealie/app.py
|
poetry run python mealie/app.py
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,6 @@ from sqlalchemy.orm.session import Session
|
||||||
|
|
||||||
from mealie.db.models.db_session import sql_global_init
|
from mealie.db.models.db_session import sql_global_init
|
||||||
|
|
||||||
sql_exists = True
|
|
||||||
|
|
||||||
SessionLocal = sql_global_init(settings.DB_URL)
|
SessionLocal = sql_global_init(settings.DB_URL)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,62 +0,0 @@
|
||||||
from mealie.core import root_logger
|
|
||||||
from mealie.core.config import settings
|
|
||||||
from mealie.core.security import get_password_hash
|
|
||||||
from mealie.db.database import db
|
|
||||||
from mealie.db.db_setup import create_session, sql_exists
|
|
||||||
from mealie.schema.settings import SiteSettings
|
|
||||||
from mealie.schema.theme import SiteTheme
|
|
||||||
from sqlalchemy.orm import Session
|
|
||||||
|
|
||||||
logger = root_logger.get_logger("init_db")
|
|
||||||
|
|
||||||
|
|
||||||
def init_db(db: Session = None) -> None:
|
|
||||||
if not db:
|
|
||||||
db = create_session()
|
|
||||||
|
|
||||||
default_group_init(db)
|
|
||||||
default_settings_init(db)
|
|
||||||
default_theme_init(db)
|
|
||||||
default_user_init(db)
|
|
||||||
|
|
||||||
db.close()
|
|
||||||
|
|
||||||
|
|
||||||
def default_theme_init(session: Session):
|
|
||||||
db.themes.create(session, SiteTheme().dict())
|
|
||||||
|
|
||||||
|
|
||||||
def default_settings_init(session: Session):
|
|
||||||
document = db.settings.create(session, SiteSettings().dict())
|
|
||||||
logger.info(f"Created Site Settings: \n {document}")
|
|
||||||
|
|
||||||
|
|
||||||
def default_group_init(session: Session):
|
|
||||||
default_group = {"name": settings.DEFAULT_GROUP}
|
|
||||||
logger.info("Generating Default Group")
|
|
||||||
db.groups.create(session, default_group)
|
|
||||||
|
|
||||||
|
|
||||||
def default_user_init(session: Session):
|
|
||||||
default_user = {
|
|
||||||
"full_name": "Change Me",
|
|
||||||
"email": settings.DEFAULT_EMAIL,
|
|
||||||
"password": get_password_hash(settings.DEFAULT_PASSWORD),
|
|
||||||
"group": settings.DEFAULT_GROUP,
|
|
||||||
"admin": True,
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.info("Generating Default User")
|
|
||||||
db.users.create(session, default_user)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
if sql_exists:
|
|
||||||
print("Database Exists")
|
|
||||||
else:
|
|
||||||
print("Database Doesn't Exists, Initializing...")
|
|
||||||
init_db()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
|
@ -13,8 +13,4 @@ def sql_global_init(db_url: str):
|
||||||
|
|
||||||
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||||
|
|
||||||
import mealie.db.models._all_models # noqa: F401
|
|
||||||
|
|
||||||
SqlAlchemyBase.metadata.create_all(engine)
|
|
||||||
|
|
||||||
return SessionLocal
|
return SessionLocal
|
||||||
|
|
|
@ -8,7 +8,7 @@ from sqlalchemy.orm import validates
|
||||||
logger = root_logger.get_logger()
|
logger = root_logger.get_logger()
|
||||||
|
|
||||||
site_settings2categories = sa.Table(
|
site_settings2categories = sa.Table(
|
||||||
"site_settings2categoories",
|
"site_settings2categories",
|
||||||
SqlAlchemyBase.metadata,
|
SqlAlchemyBase.metadata,
|
||||||
sa.Column("sidebar_id", sa.Integer, sa.ForeignKey("site_settings.id")),
|
sa.Column("sidebar_id", sa.Integer, sa.ForeignKey("site_settings.id")),
|
||||||
sa.Column("category_id", sa.String, sa.ForeignKey("categories.id")),
|
sa.Column("category_id", sa.String, sa.ForeignKey("categories.id")),
|
||||||
|
|
|
@ -11,11 +11,7 @@ recipes2tags = sa.Table(
|
||||||
"recipes2tags",
|
"recipes2tags",
|
||||||
SqlAlchemyBase.metadata,
|
SqlAlchemyBase.metadata,
|
||||||
sa.Column("recipe_id", sa.Integer, sa.ForeignKey("recipes.id")),
|
sa.Column("recipe_id", sa.Integer, sa.ForeignKey("recipes.id")),
|
||||||
<<<<<<< HEAD
|
|
||||||
sa.Column("tag_id", sa.Integer, sa.ForeignKey("tags.id")),
|
sa.Column("tag_id", sa.Integer, sa.ForeignKey("tags.id")),
|
||||||
=======
|
|
||||||
sa.Column("tag_slug", sa.String, sa.ForeignKey("tags.slug")),
|
|
||||||
>>>>>>> e2e2ad5 (feat: other databases support)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
BIN
mealie/mealie.db
Normal file
BIN
mealie/mealie.db
Normal file
Binary file not shown.
|
@ -7,7 +7,7 @@ ARG1=${1:-production}
|
||||||
# DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
|
# DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
|
||||||
|
|
||||||
# # Initialize Database Prerun
|
# # Initialize Database Prerun
|
||||||
poetry run python /app/mealie/db/init_db.py
|
poetry run alembic upgrade head
|
||||||
poetry run python /app/mealie/services/image/minify.py
|
poetry run python /app/mealie/services/image/minify.py
|
||||||
|
|
||||||
# Migrations
|
# Migrations
|
||||||
|
|
|
@ -16,6 +16,7 @@ class SiteSettings(CamelModel):
|
||||||
class Config:
|
class Config:
|
||||||
orm_mode = True
|
orm_mode = True
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
schema_extra = {
|
schema_extra = {
|
||||||
"example": {
|
"example": {
|
||||||
"language": "en",
|
"language": "en",
|
||||||
|
@ -29,6 +30,8 @@ class SiteSettings(CamelModel):
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
=======
|
||||||
|
>>>>>>> 0afdaf6 (Add alembic)
|
||||||
|
|
||||||
class CustomPageBase(CamelModel):
|
class CustomPageBase(CamelModel):
|
||||||
name: str
|
name: str
|
||||||
|
|
|
@ -20,17 +20,3 @@ class SiteTheme(BaseModel):
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
orm_mode = True
|
orm_mode = True
|
||||||
schema_extra = {
|
|
||||||
"example": {
|
|
||||||
"name": "default",
|
|
||||||
"colors": {
|
|
||||||
"primary": "#E58325",
|
|
||||||
"accent": "#00457A",
|
|
||||||
"secondary": "#973542",
|
|
||||||
"success": "#5AB1BB",
|
|
||||||
"info": "#4990BA",
|
|
||||||
"warning": "#FF4081",
|
|
||||||
"error": "#EF5350",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
4202
poetry.lock
generated
4202
poetry.lock
generated
File diff suppressed because it is too large
Load diff
127
pyproject.toml
127
pyproject.toml
|
@ -1,64 +1,65 @@
|
||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "mealie"
|
name = "mealie"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
description = "A Recipe Manager"
|
description = "A Recipe Manager"
|
||||||
authors = ["Hayden <hay-kot@pm.me>"]
|
authors = ["Hayden <hay-kot@pm.me>"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|
||||||
[tool.poetry.scripts]
|
[tool.poetry.scripts]
|
||||||
start = "mealie.app:main"
|
start = "mealie.app:main"
|
||||||
|
|
||||||
[tool.poetry.dependencies]
|
[tool.poetry.dependencies]
|
||||||
python = "^3.9"
|
python = "^3.9"
|
||||||
aiofiles = "0.5.0"
|
aiofiles = "0.5.0"
|
||||||
aniso8601 = "7.0.0"
|
aniso8601 = "7.0.0"
|
||||||
appdirs = "1.4.4"
|
appdirs = "1.4.4"
|
||||||
fastapi = "^0.63.0"
|
fastapi = "^0.63.0"
|
||||||
uvicorn = {extras = ["standard"], version = "^0.13.0"}
|
uvicorn = {extras = ["standard"], version = "^0.13.0"}
|
||||||
APScheduler = "^3.6.3"
|
APScheduler = "^3.6.3"
|
||||||
SQLAlchemy = "^1.3.22"
|
SQLAlchemy = "^1.3.22"
|
||||||
Jinja2 = "^2.11.2"
|
Jinja2 = "^2.11.2"
|
||||||
python-dotenv = "^0.15.0"
|
python-dotenv = "^0.15.0"
|
||||||
python-slugify = "^4.0.1"
|
python-slugify = "^4.0.1"
|
||||||
requests = "^2.25.1"
|
requests = "^2.25.1"
|
||||||
PyYAML = "^5.3.1"
|
PyYAML = "^5.3.1"
|
||||||
extruct = "^0.12.0"
|
extruct = "^0.12.0"
|
||||||
scrape-schema-recipe = "^0.1.3"
|
scrape-schema-recipe = "^0.1.3"
|
||||||
python-multipart = "^0.0.5"
|
python-multipart = "^0.0.5"
|
||||||
fastapi-camelcase = "^1.0.2"
|
fastapi-camelcase = "^1.0.2"
|
||||||
bcrypt = "^3.2.0"
|
bcrypt = "^3.2.0"
|
||||||
python-jose = "^3.2.0"
|
python-jose = "^3.2.0"
|
||||||
passlib = "^1.7.4"
|
passlib = "^1.7.4"
|
||||||
lxml = "4.6.2"
|
lxml = "4.6.2"
|
||||||
Pillow = "^8.2.0"
|
Pillow = "^8.2.0"
|
||||||
pathvalidate = "^2.4.1"
|
pathvalidate = "^2.4.1"
|
||||||
|
alembic = "^1.5.8"
|
||||||
|
|
||||||
[tool.poetry.dev-dependencies]
|
|
||||||
pylint = "^2.6.0"
|
[tool.poetry.dev-dependencies]
|
||||||
black = "^20.8b1"
|
pylint = "^2.6.0"
|
||||||
pytest = "^6.2.1"
|
black = "^20.8b1"
|
||||||
pytest-cov = "^2.11.0"
|
pytest = "^6.2.1"
|
||||||
mkdocs-material = "^7.0.2"
|
pytest-cov = "^2.11.0"
|
||||||
flake8 = "^3.9.0"
|
mkdocs-material = "^7.0.2"
|
||||||
coverage = "^5.5"
|
flake8 = "^3.9.0"
|
||||||
|
coverage = "^5.5"
|
||||||
[build-system]
|
|
||||||
requires = ["poetry-core>=1.0.0"]
|
[build-system]
|
||||||
build-backend = "poetry.core.masonry.api"
|
requires = ["poetry-core>=1.0.0"]
|
||||||
|
build-backend = "poetry.core.masonry.api"
|
||||||
[tool.black]
|
|
||||||
line-length = 120
|
[tool.black]
|
||||||
|
line-length = 120
|
||||||
[tool.pytest.ini_options]
|
|
||||||
minversion = "6.0"
|
[tool.pytest.ini_options]
|
||||||
addopts = "-ra -q --cov=mealie"
|
minversion = "6.0"
|
||||||
python_files = 'test_*'
|
addopts = "-ra -q --cov=mealie"
|
||||||
python_classes = '*Tests'
|
python_files = 'test_*'
|
||||||
python_functions = 'test_*'
|
python_classes = '*Tests'
|
||||||
testpaths = [
|
python_functions = 'test_*'
|
||||||
"tests",
|
testpaths = [
|
||||||
]
|
"tests",
|
||||||
|
]
|
||||||
[tool.coverage.report]
|
|
||||||
|
[tool.coverage.report]
|
||||||
skip_empty = true
|
skip_empty = true
|
|
@ -5,7 +5,6 @@ from fastapi.testclient import TestClient
|
||||||
from mealie.app import app
|
from mealie.app import app
|
||||||
from mealie.core.config import app_dirs, settings
|
from mealie.core.config import app_dirs, settings
|
||||||
from mealie.db.db_setup import generate_session, sql_global_init
|
from mealie.db.db_setup import generate_session, sql_global_init
|
||||||
from mealie.db.init_db import init_db
|
|
||||||
from pytest import fixture
|
from pytest import fixture
|
||||||
|
|
||||||
from tests.app_routes import AppRoutes
|
from tests.app_routes import AppRoutes
|
||||||
|
@ -17,7 +16,6 @@ SQLITE_FILE.unlink(missing_ok=True)
|
||||||
|
|
||||||
|
|
||||||
TestSessionLocal = sql_global_init(SQLITE_FILE)
|
TestSessionLocal = sql_global_init(SQLITE_FILE)
|
||||||
init_db(TestSessionLocal())
|
|
||||||
|
|
||||||
|
|
||||||
def override_get_db():
|
def override_get_db():
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue