Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .pre-commit-config.yaml
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,6 +47,13 @@ repos:
language: unsupported
pass_filenames: false

- id: local-ty
name: ty check
entry: uv run ty check backend/app
require_serial: true
language: unsupported
pass_filenames: false

- id: generate-frontend-sdk
name: Generate Frontend SDK
entry: bash ./scripts/generate-client.sh
Expand Down
1 change: 1 addition & 0 deletions backend/app/alembic/env.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -63,6 +63,7 @@ def run_migrations_online():

"""
configuration = config.get_section(config.config_ini_section)
assert configuration is not None
configuration["sqlalchemy.url"] = get_url()
connectable = engine_from_config(
configuration,
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -29,7 +29,7 @@ def upgrade():

def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(None, 'item', type_='foreignkey')
op.drop_constraint('item_owner_id_fkey', 'item', type_='foreignkey')

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A little confused about this one, as drop_constraint requires a str constraint name. Not sure why it was None before and I don't see how it would have worked. Feels like this is an actual bug fix? 🤔

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm confused too, this is generated automatically, don't really know what it might be... but also, all the other op.drop_constraint() calls have a name, and tests seem to pass, so we're probably good. ✔️

op.create_foreign_key('item_owner_id_fkey', 'item', 'user', ['owner_id'], ['id'])
op.alter_column('item', 'owner_id',
existing_type=sa.UUID(),
Expand Down
3 changes: 2 additions & 1 deletion backend/app/api/routes/items.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -41,7 +41,8 @@ def read_items(
)
items = session.exec(statement).all()

return ItemsPublic(data=items, count=count)
items_public = [ItemPublic.model_validate(item) for item in items]
return ItemsPublic(data=items_public, count=count)


@router.get("/{id}", response_model=ItemPublic)
Expand Down
3 changes: 2 additions & 1 deletion backend/app/api/routes/users.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,7 +47,8 @@ def read_users(session: SessionDep, skip: int = 0, limit: int = 100) -> Any:
)
users = session.exec(statement).all()

return UsersPublic(data=users, count=count)
users_public = [UserPublic.model_validate(user) for user in users]
return UsersPublic(data=users_public, count=count)


@router.post(
Expand Down
4 changes: 2 additions & 2 deletions backend/app/models.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -31,7 +31,7 @@ class UserRegister(SQLModel):

# Properties to receive via API on update, all are optional
class UserUpdate(UserBase):
email: EmailStr | None = Field(default=None, max_length=255) # type: ignore
email: EmailStr | None = Field(default=None, max_length=255) # type: ignore[assignment]
password: str | None = Field(default=None, min_length=8, max_length=128)


Expand DownExpand Up@@ -80,7 +80,7 @@ class ItemCreate(ItemBase):

# Properties to receive on item update
class ItemUpdate(ItemBase):
title: str | None = Field(default=None, min_length=1, max_length=255) # type: ignore
title: str | None = Field(default=None, min_length=1, max_length=255) # type: ignore[assignment]


# Database model, database table inferred from class name
Expand Down
2 changes: 1 addition & 1 deletion backend/app/utils.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,7 +4,7 @@
from pathlib import Path
from typing import Any

import emails # type: ignore
import emails # type: ignore[import-untyped]
import jwt
from jinja2 import Template
from jwt.exceptions import InvalidTokenError
Expand Down
4 changes: 4 additions & 0 deletions backend/pyproject.toml
Original file line numberDiff line numberDiff line change
Expand Up@@ -25,6 +25,7 @@ dependencies = [
dev = [
"pytest<8.0.0,>=7.4.3",
"mypy<2.0.0,>=1.8.0",
"ty>=0.0.25",
"ruff<1.0.0,>=0.2.2",
"prek>=0.2.24,<1.0.0",
"coverage<8.0.0,>=7.4.3",
Expand DownExpand Up@@ -75,3 +76,6 @@ sort = "-Cover"

[tool.coverage.html]
show_contexts = true

[tool.ty.terminal]
error-on-warning = true
1 change: 1 addition & 0 deletions backend/scripts/lint.sh
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,5 +4,6 @@ set -e
set -x

mypy app
ty check app
ruff check app
ruff format app --check
26 changes: 26 additions & 0 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .pre-commit-config.yaml
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,6 +47,13 @@ repos:
language: unsupported
pass_filenames: false

- id: local-ty
name: ty check
entry: uv run ty check backend/app
require_serial: true
language: unsupported
pass_filenames: false

- id: generate-frontend-sdk
name: Generate Frontend SDK
entry: bash ./scripts/generate-client.sh
Expand Down
1 change: 1 addition & 0 deletions backend/app/alembic/env.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -63,6 +63,7 @@ def run_migrations_online():

"""
configuration = config.get_section(config.config_ini_section)
assert configuration is not None
configuration["sqlalchemy.url"] = get_url()
connectable = engine_from_config(
configuration,
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -29,7 +29,7 @@ def upgrade():

def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(None, 'item', type_='foreignkey')
op.drop_constraint('item_owner_id_fkey', 'item', type_='foreignkey')

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A little confused about this one, as drop_constraint requires a str constraint name. Not sure why it was None before and I don't see how it would have worked. Feels like this is an actual bug fix? 🤔

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm confused too, this is generated automatically, don't really know what it might be... but also, all the other op.drop_constraint() calls have a name, and tests seem to pass, so we're probably good. ✔️

op.create_foreign_key('item_owner_id_fkey', 'item', 'user', ['owner_id'], ['id'])
op.alter_column('item', 'owner_id',
existing_type=sa.UUID(),
Expand Down
3 changes: 2 additions & 1 deletion backend/app/api/routes/items.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -41,7 +41,8 @@ def read_items(
)
items = session.exec(statement).all()

return ItemsPublic(data=items, count=count)
items_public = [ItemPublic.model_validate(item) for item in items]
return ItemsPublic(data=items_public, count=count)


@router.get("/{id}", response_model=ItemPublic)
Expand Down
3 changes: 2 additions & 1 deletion backend/app/api/routes/users.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,7 +47,8 @@ def read_users(session: SessionDep, skip: int = 0, limit: int = 100) -> Any:
)
users = session.exec(statement).all()

return UsersPublic(data=users, count=count)
users_public = [UserPublic.model_validate(user) for user in users]
return UsersPublic(data=users_public, count=count)


@router.post(
Expand Down
4 changes: 2 additions & 2 deletions backend/app/models.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -31,7 +31,7 @@ class UserRegister(SQLModel):

# Properties to receive via API on update, all are optional
class UserUpdate(UserBase):
email: EmailStr | None = Field(default=None, max_length=255) # type: ignore
email: EmailStr | None = Field(default=None, max_length=255) # type: ignore[assignment]
password: str | None = Field(default=None, min_length=8, max_length=128)


Expand DownExpand Up@@ -80,7 +80,7 @@ class ItemCreate(ItemBase):

# Properties to receive on item update
class ItemUpdate(ItemBase):
title: str | None = Field(default=None, min_length=1, max_length=255) # type: ignore
title: str | None = Field(default=None, min_length=1, max_length=255) # type: ignore[assignment]


# Database model, database table inferred from class name
Expand Down
2 changes: 1 addition & 1 deletion backend/app/utils.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,7 +4,7 @@
from pathlib import Path
from typing import Any

import emails # type: ignore
import emails # type: ignore[import-untyped]
import jwt
from jinja2 import Template
from jwt.exceptions import InvalidTokenError
Expand Down
4 changes: 4 additions & 0 deletions backend/pyproject.toml
Original file line numberDiff line numberDiff line change
Expand Up@@ -25,6 +25,7 @@ dependencies = [
dev = [
"pytest<8.0.0,>=7.4.3",
"mypy<2.0.0,>=1.8.0",
"ty>=0.0.25",
"ruff<1.0.0,>=0.2.2",
"prek>=0.2.24,<1.0.0",
"coverage<8.0.0,>=7.4.3",
Expand DownExpand Up@@ -75,3 +76,6 @@ sort = "-Cover"

[tool.coverage.html]
show_contexts = true

[tool.ty.terminal]
error-on-warning = true
1 change: 1 addition & 0 deletions backend/scripts/lint.sh
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,5 +4,6 @@ set -e
set -x

mypy app
ty check app
ruff check app
ruff format app --check
26 changes: 26 additions & 0 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .pre-commit-config.yaml
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,6 +47,13 @@ repos:
language: unsupported
pass_filenames: false

- id: local-ty
name: ty check
entry: uv run ty check backend/app
require_serial: true
language: unsupported
pass_filenames: false

- id: generate-frontend-sdk
name: Generate Frontend SDK
entry: bash ./scripts/generate-client.sh
Expand Down
1 change: 1 addition & 0 deletions backend/app/alembic/env.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -63,6 +63,7 @@ def run_migrations_online():

"""
configuration = config.get_section(config.config_ini_section)
assert configuration is not None
configuration["sqlalchemy.url"] = get_url()
connectable = engine_from_config(
configuration,
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -29,7 +29,7 @@ def upgrade():

def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(None, 'item', type_='foreignkey')
op.drop_constraint('item_owner_id_fkey', 'item', type_='foreignkey')

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A little confused about this one, as drop_constraint requires a str constraint name. Not sure why it was None before and I don't see how it would have worked. Feels like this is an actual bug fix? 🤔

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm confused too, this is generated automatically, don't really know what it might be... but also, all the other op.drop_constraint() calls have a name, and tests seem to pass, so we're probably good. ✔️

op.create_foreign_key('item_owner_id_fkey', 'item', 'user', ['owner_id'], ['id'])
op.alter_column('item', 'owner_id',
existing_type=sa.UUID(),
Expand Down
3 changes: 2 additions & 1 deletion backend/app/api/routes/items.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -41,7 +41,8 @@ def read_items(
)
items = session.exec(statement).all()

return ItemsPublic(data=items, count=count)
items_public = [ItemPublic.model_validate(item) for item in items]
return ItemsPublic(data=items_public, count=count)


@router.get("/{id}", response_model=ItemPublic)
Expand Down
3 changes: 2 additions & 1 deletion backend/app/api/routes/users.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,7 +47,8 @@ def read_users(session: SessionDep, skip: int = 0, limit: int = 100) -> Any:
)
users = session.exec(statement).all()

return UsersPublic(data=users, count=count)
users_public = [UserPublic.model_validate(user) for user in users]
return UsersPublic(data=users_public, count=count)


@router.post(
Expand Down
4 changes: 2 additions & 2 deletions backend/app/models.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -31,7 +31,7 @@ class UserRegister(SQLModel):

# Properties to receive via API on update, all are optional
class UserUpdate(UserBase):
email: EmailStr | None = Field(default=None, max_length=255) # type: ignore
email: EmailStr | None = Field(default=None, max_length=255) # type: ignore[assignment]
password: str | None = Field(default=None, min_length=8, max_length=128)


Expand DownExpand Up@@ -80,7 +80,7 @@ class ItemCreate(ItemBase):

# Properties to receive on item update
class ItemUpdate(ItemBase):
title: str | None = Field(default=None, min_length=1, max_length=255) # type: ignore
title: str | None = Field(default=None, min_length=1, max_length=255) # type: ignore[assignment]


# Database model, database table inferred from class name
Expand Down
2 changes: 1 addition & 1 deletion backend/app/utils.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,7 +4,7 @@
from pathlib import Path
from typing import Any

import emails # type: ignore
import emails # type: ignore[import-untyped]
import jwt
from jinja2 import Template
from jwt.exceptions import InvalidTokenError
Expand Down
4 changes: 4 additions & 0 deletions backend/pyproject.toml
Original file line numberDiff line numberDiff line change
Expand Up@@ -25,6 +25,7 @@ dependencies = [
dev = [
"pytest<8.0.0,>=7.4.3",
"mypy<2.0.0,>=1.8.0",
"ty>=0.0.25",
"ruff<1.0.0,>=0.2.2",
"prek>=0.2.24,<1.0.0",
"coverage<8.0.0,>=7.4.3",
Expand DownExpand Up@@ -75,3 +76,6 @@ sort = "-Cover"

[tool.coverage.html]
show_contexts = true

[tool.ty.terminal]
error-on-warning = true
1 change: 1 addition & 0 deletions backend/scripts/lint.sh
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,5 +4,6 @@ set -e
set -x

mypy app
ty check app
ruff check app
ruff format app --check
26 changes: 26 additions & 0 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .pre-commit-config.yaml
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,6 +47,13 @@ repos:
language: unsupported
pass_filenames: false

- id: local-ty
name: ty check
entry: uv run ty check backend/app
require_serial: true
language: unsupported
pass_filenames: false

- id: generate-frontend-sdk
name: Generate Frontend SDK
entry: bash ./scripts/generate-client.sh
Expand Down
1 change: 1 addition & 0 deletions backend/app/alembic/env.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -63,6 +63,7 @@ def run_migrations_online():

"""
configuration = config.get_section(config.config_ini_section)
assert configuration is not None
configuration["sqlalchemy.url"] = get_url()
connectable = engine_from_config(
configuration,
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -29,7 +29,7 @@ def upgrade():

def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(None, 'item', type_='foreignkey')
op.drop_constraint('item_owner_id_fkey', 'item', type_='foreignkey')

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A little confused about this one, as drop_constraint requires a str constraint name. Not sure why it was None before and I don't see how it would have worked. Feels like this is an actual bug fix? 🤔

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm confused too, this is generated automatically, don't really know what it might be... but also, all the other op.drop_constraint() calls have a name, and tests seem to pass, so we're probably good. ✔️

op.create_foreign_key('item_owner_id_fkey', 'item', 'user', ['owner_id'], ['id'])
op.alter_column('item', 'owner_id',
existing_type=sa.UUID(),
Expand Down
3 changes: 2 additions & 1 deletion backend/app/api/routes/items.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -41,7 +41,8 @@ def read_items(
)
items = session.exec(statement).all()

return ItemsPublic(data=items, count=count)
items_public = [ItemPublic.model_validate(item) for item in items]
return ItemsPublic(data=items_public, count=count)


@router.get("/{id}", response_model=ItemPublic)
Expand Down
3 changes: 2 additions & 1 deletion backend/app/api/routes/users.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,7 +47,8 @@ def read_users(session: SessionDep, skip: int = 0, limit: int = 100) -> Any:
)
users = session.exec(statement).all()

return UsersPublic(data=users, count=count)
users_public = [UserPublic.model_validate(user) for user in users]
return UsersPublic(data=users_public, count=count)


@router.post(
Expand Down
4 changes: 2 additions & 2 deletions backend/app/models.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -31,7 +31,7 @@ class UserRegister(SQLModel):

# Properties to receive via API on update, all are optional
class UserUpdate(UserBase):
email: EmailStr | None = Field(default=None, max_length=255) # type: ignore
email: EmailStr | None = Field(default=None, max_length=255) # type: ignore[assignment]
password: str | None = Field(default=None, min_length=8, max_length=128)


Expand DownExpand Up@@ -80,7 +80,7 @@ class ItemCreate(ItemBase):

# Properties to receive on item update
class ItemUpdate(ItemBase):
title: str | None = Field(default=None, min_length=1, max_length=255) # type: ignore
title: str | None = Field(default=None, min_length=1, max_length=255) # type: ignore[assignment]


# Database model, database table inferred from class name
Expand Down
2 changes: 1 addition & 1 deletion backend/app/utils.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,7 +4,7 @@
from pathlib import Path
from typing import Any

import emails # type: ignore
import emails # type: ignore[import-untyped]
import jwt
from jinja2 import Template
from jwt.exceptions import InvalidTokenError
Expand Down
4 changes: 4 additions & 0 deletions backend/pyproject.toml
Original file line numberDiff line numberDiff line change
Expand Up@@ -25,6 +25,7 @@ dependencies = [
dev = [
"pytest<8.0.0,>=7.4.3",
"mypy<2.0.0,>=1.8.0",
"ty>=0.0.25",
"ruff<1.0.0,>=0.2.2",
"prek>=0.2.24,<1.0.0",
"coverage<8.0.0,>=7.4.3",
Expand DownExpand Up@@ -75,3 +76,6 @@ sort = "-Cover"

[tool.coverage.html]
show_contexts = true

[tool.ty.terminal]
error-on-warning = true
1 change: 1 addition & 0 deletions backend/scripts/lint.sh
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,5 +4,6 @@ set -e
set -x

mypy app
ty check app
ruff check app
ruff format app --check
26 changes: 26 additions & 0 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .pre-commit-config.yaml
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,6 +47,13 @@ repos:
language: unsupported
pass_filenames: false

- id: local-ty
name: ty check
entry: uv run ty check backend/app
require_serial: true
language: unsupported
pass_filenames: false

- id: generate-frontend-sdk
name: Generate Frontend SDK
entry: bash ./scripts/generate-client.sh
Expand Down
1 change: 1 addition & 0 deletions backend/app/alembic/env.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -63,6 +63,7 @@ def run_migrations_online():

"""
configuration = config.get_section(config.config_ini_section)
assert configuration is not None
configuration["sqlalchemy.url"] = get_url()
connectable = engine_from_config(
configuration,
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -29,7 +29,7 @@ def upgrade():

def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(None, 'item', type_='foreignkey')
op.drop_constraint('item_owner_id_fkey', 'item', type_='foreignkey')

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A little confused about this one, as drop_constraint requires a str constraint name. Not sure why it was None before and I don't see how it would have worked. Feels like this is an actual bug fix? 🤔

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm confused too, this is generated automatically, don't really know what it might be... but also, all the other op.drop_constraint() calls have a name, and tests seem to pass, so we're probably good. ✔️

op.create_foreign_key('item_owner_id_fkey', 'item', 'user', ['owner_id'], ['id'])
op.alter_column('item', 'owner_id',
existing_type=sa.UUID(),
Expand Down
3 changes: 2 additions & 1 deletion backend/app/api/routes/items.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -41,7 +41,8 @@ def read_items(
)
items = session.exec(statement).all()

return ItemsPublic(data=items, count=count)
items_public = [ItemPublic.model_validate(item) for item in items]
return ItemsPublic(data=items_public, count=count)


@router.get("/{id}", response_model=ItemPublic)
Expand Down
3 changes: 2 additions & 1 deletion backend/app/api/routes/users.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,7 +47,8 @@ def read_users(session: SessionDep, skip: int = 0, limit: int = 100) -> Any:
)
users = session.exec(statement).all()

return UsersPublic(data=users, count=count)
users_public = [UserPublic.model_validate(user) for user in users]
return UsersPublic(data=users_public, count=count)


@router.post(
Expand Down
4 changes: 2 additions & 2 deletions backend/app/models.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -31,7 +31,7 @@ class UserRegister(SQLModel):

# Properties to receive via API on update, all are optional
class UserUpdate(UserBase):
email: EmailStr | None = Field(default=None, max_length=255) # type: ignore
email: EmailStr | None = Field(default=None, max_length=255) # type: ignore[assignment]
password: str | None = Field(default=None, min_length=8, max_length=128)


Expand DownExpand Up@@ -80,7 +80,7 @@ class ItemCreate(ItemBase):

# Properties to receive on item update
class ItemUpdate(ItemBase):
title: str | None = Field(default=None, min_length=1, max_length=255) # type: ignore
title: str | None = Field(default=None, min_length=1, max_length=255) # type: ignore[assignment]


# Database model, database table inferred from class name
Expand Down
2 changes: 1 addition & 1 deletion backend/app/utils.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,7 +4,7 @@
from pathlib import Path
from typing import Any

import emails # type: ignore
import emails # type: ignore[import-untyped]
import jwt
from jinja2 import Template
from jwt.exceptions import InvalidTokenError
Expand Down
4 changes: 4 additions & 0 deletions backend/pyproject.toml
Original file line numberDiff line numberDiff line change
Expand Up@@ -25,6 +25,7 @@ dependencies = [
dev = [
"pytest<8.0.0,>=7.4.3",
"mypy<2.0.0,>=1.8.0",
"ty>=0.0.25",
"ruff<1.0.0,>=0.2.2",
"prek>=0.2.24,<1.0.0",
"coverage<8.0.0,>=7.4.3",
Expand DownExpand Up@@ -75,3 +76,6 @@ sort = "-Cover"

[tool.coverage.html]
show_contexts = true

[tool.ty.terminal]
error-on-warning = true
1 change: 1 addition & 0 deletions backend/scripts/lint.sh
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,5 +4,6 @@ set -e
set -x

mypy app
ty check app
ruff check app
ruff format app --check
26 changes: 26 additions & 0 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .pre-commit-config.yaml
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,6 +47,13 @@ repos:
language: unsupported
pass_filenames: false

- id: local-ty
name: ty check
entry: uv run ty check backend/app
require_serial: true
language: unsupported
pass_filenames: false

- id: generate-frontend-sdk
name: Generate Frontend SDK
entry: bash ./scripts/generate-client.sh
Expand Down
1 change: 1 addition & 0 deletions backend/app/alembic/env.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -63,6 +63,7 @@ def run_migrations_online():

"""
configuration = config.get_section(config.config_ini_section)
assert configuration is not None
configuration["sqlalchemy.url"] = get_url()
connectable = engine_from_config(
configuration,
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -29,7 +29,7 @@ def upgrade():

def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(None, 'item', type_='foreignkey')
op.drop_constraint('item_owner_id_fkey', 'item', type_='foreignkey')

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A little confused about this one, as drop_constraint requires a str constraint name. Not sure why it was None before and I don't see how it would have worked. Feels like this is an actual bug fix? 🤔

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm confused too, this is generated automatically, don't really know what it might be... but also, all the other op.drop_constraint() calls have a name, and tests seem to pass, so we're probably good. ✔️

op.create_foreign_key('item_owner_id_fkey', 'item', 'user', ['owner_id'], ['id'])
op.alter_column('item', 'owner_id',
existing_type=sa.UUID(),
Expand Down
3 changes: 2 additions & 1 deletion backend/app/api/routes/items.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -41,7 +41,8 @@ def read_items(
)
items = session.exec(statement).all()

return ItemsPublic(data=items, count=count)
items_public = [ItemPublic.model_validate(item) for item in items]
return ItemsPublic(data=items_public, count=count)


@router.get("/{id}", response_model=ItemPublic)
Expand Down
3 changes: 2 additions & 1 deletion backend/app/api/routes/users.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,7 +47,8 @@ def read_users(session: SessionDep, skip: int = 0, limit: int = 100) -> Any:
)
users = session.exec(statement).all()

return UsersPublic(data=users, count=count)
users_public = [UserPublic.model_validate(user) for user in users]
return UsersPublic(data=users_public, count=count)


@router.post(
Expand Down
4 changes: 2 additions & 2 deletions backend/app/models.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -31,7 +31,7 @@ class UserRegister(SQLModel):

# Properties to receive via API on update, all are optional
class UserUpdate(UserBase):
email: EmailStr | None = Field(default=None, max_length=255) # type: ignore
email: EmailStr | None = Field(default=None, max_length=255) # type: ignore[assignment]
password: str | None = Field(default=None, min_length=8, max_length=128)


Expand DownExpand Up@@ -80,7 +80,7 @@ class ItemCreate(ItemBase):

# Properties to receive on item update
class ItemUpdate(ItemBase):
title: str | None = Field(default=None, min_length=1, max_length=255) # type: ignore
title: str | None = Field(default=None, min_length=1, max_length=255) # type: ignore[assignment]


# Database model, database table inferred from class name
Expand Down
2 changes: 1 addition & 1 deletion backend/app/utils.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,7 +4,7 @@
from pathlib import Path
from typing import Any

import emails # type: ignore
import emails # type: ignore[import-untyped]
import jwt
from jinja2 import Template
from jwt.exceptions import InvalidTokenError
Expand Down
4 changes: 4 additions & 0 deletions backend/pyproject.toml
Original file line numberDiff line numberDiff line change
Expand Up@@ -25,6 +25,7 @@ dependencies = [
dev = [
"pytest<8.0.0,>=7.4.3",
"mypy<2.0.0,>=1.8.0",
"ty>=0.0.25",
"ruff<1.0.0,>=0.2.2",
"prek>=0.2.24,<1.0.0",
"coverage<8.0.0,>=7.4.3",
Expand DownExpand Up@@ -75,3 +76,6 @@ sort = "-Cover"

[tool.coverage.html]
show_contexts = true

[tool.ty.terminal]
error-on-warning = true
1 change: 1 addition & 0 deletions backend/scripts/lint.sh
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,5 +4,6 @@ set -e
set -x

mypy app
ty check app
ruff check app
ruff format app --check
26 changes: 26 additions & 0 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .pre-commit-config.yaml
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,6 +47,13 @@ repos:
language: unsupported
pass_filenames: false

- id: local-ty
name: ty check
entry: uv run ty check backend/app
require_serial: true
language: unsupported
pass_filenames: false

- id: generate-frontend-sdk
name: Generate Frontend SDK
entry: bash ./scripts/generate-client.sh
Expand Down
1 change: 1 addition & 0 deletions backend/app/alembic/env.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -63,6 +63,7 @@ def run_migrations_online():

"""
configuration = config.get_section(config.config_ini_section)
assert configuration is not None
configuration["sqlalchemy.url"] = get_url()
connectable = engine_from_config(
configuration,
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -29,7 +29,7 @@ def upgrade():

def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(None, 'item', type_='foreignkey')
op.drop_constraint('item_owner_id_fkey', 'item', type_='foreignkey')

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A little confused about this one, as drop_constraint requires a str constraint name. Not sure why it was None before and I don't see how it would have worked. Feels like this is an actual bug fix? 🤔

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm confused too, this is generated automatically, don't really know what it might be... but also, all the other op.drop_constraint() calls have a name, and tests seem to pass, so we're probably good. ✔️

op.create_foreign_key('item_owner_id_fkey', 'item', 'user', ['owner_id'], ['id'])
op.alter_column('item', 'owner_id',
existing_type=sa.UUID(),
Expand Down
3 changes: 2 additions & 1 deletion backend/app/api/routes/items.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -41,7 +41,8 @@ def read_items(
)
items = session.exec(statement).all()

return ItemsPublic(data=items, count=count)
items_public = [ItemPublic.model_validate(item) for item in items]
return ItemsPublic(data=items_public, count=count)


@router.get("/{id}", response_model=ItemPublic)
Expand Down
3 changes: 2 additions & 1 deletion backend/app/api/routes/users.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,7 +47,8 @@ def read_users(session: SessionDep, skip: int = 0, limit: int = 100) -> Any:
)
users = session.exec(statement).all()

return UsersPublic(data=users, count=count)
users_public = [UserPublic.model_validate(user) for user in users]
return UsersPublic(data=users_public, count=count)


@router.post(
Expand Down
4 changes: 2 additions & 2 deletions backend/app/models.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -31,7 +31,7 @@ class UserRegister(SQLModel):

# Properties to receive via API on update, all are optional
class UserUpdate(UserBase):
email: EmailStr | None = Field(default=None, max_length=255) # type: ignore
email: EmailStr | None = Field(default=None, max_length=255) # type: ignore[assignment]
password: str | None = Field(default=None, min_length=8, max_length=128)


Expand DownExpand Up@@ -80,7 +80,7 @@ class ItemCreate(ItemBase):

# Properties to receive on item update
class ItemUpdate(ItemBase):
title: str | None = Field(default=None, min_length=1, max_length=255) # type: ignore
title: str | None = Field(default=None, min_length=1, max_length=255) # type: ignore[assignment]


# Database model, database table inferred from class name
Expand Down
2 changes: 1 addition & 1 deletion backend/app/utils.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,7 +4,7 @@
from pathlib import Path
from typing import Any

import emails # type: ignore
import emails # type: ignore[import-untyped]
import jwt
from jinja2 import Template
from jwt.exceptions import InvalidTokenError
Expand Down
4 changes: 4 additions & 0 deletions backend/pyproject.toml
Original file line numberDiff line numberDiff line change
Expand Up@@ -25,6 +25,7 @@ dependencies = [
dev = [
"pytest<8.0.0,>=7.4.3",
"mypy<2.0.0,>=1.8.0",
"ty>=0.0.25",
"ruff<1.0.0,>=0.2.2",
"prek>=0.2.24,<1.0.0",
"coverage<8.0.0,>=7.4.3",
Expand DownExpand Up@@ -75,3 +76,6 @@ sort = "-Cover"

[tool.coverage.html]
show_contexts = true

[tool.ty.terminal]
error-on-warning = true
1 change: 1 addition & 0 deletions backend/scripts/lint.sh
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,5 +4,6 @@ set -e
set -x

mypy app
ty check app
ruff check app
ruff format app --check
26 changes: 26 additions & 0 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .pre-commit-config.yaml
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,6 +47,13 @@ repos:
language: unsupported
pass_filenames: false

- id: local-ty
name: ty check
entry: uv run ty check backend/app
require_serial: true
language: unsupported
pass_filenames: false

- id: generate-frontend-sdk
name: Generate Frontend SDK
entry: bash ./scripts/generate-client.sh
Expand Down
1 change: 1 addition & 0 deletions backend/app/alembic/env.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -63,6 +63,7 @@ def run_migrations_online():

"""
configuration = config.get_section(config.config_ini_section)
assert configuration is not None
configuration["sqlalchemy.url"] = get_url()
connectable = engine_from_config(
configuration,
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -29,7 +29,7 @@ def upgrade():

def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(None, 'item', type_='foreignkey')
op.drop_constraint('item_owner_id_fkey', 'item', type_='foreignkey')

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A little confused about this one, as drop_constraint requires a str constraint name. Not sure why it was None before and I don't see how it would have worked. Feels like this is an actual bug fix? 🤔

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm confused too, this is generated automatically, don't really know what it might be... but also, all the other op.drop_constraint() calls have a name, and tests seem to pass, so we're probably good. ✔️

op.create_foreign_key('item_owner_id_fkey', 'item', 'user', ['owner_id'], ['id'])
op.alter_column('item', 'owner_id',
existing_type=sa.UUID(),
Expand Down
3 changes: 2 additions & 1 deletion backend/app/api/routes/items.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -41,7 +41,8 @@ def read_items(
)
items = session.exec(statement).all()

return ItemsPublic(data=items, count=count)
items_public = [ItemPublic.model_validate(item) for item in items]
return ItemsPublic(data=items_public, count=count)


@router.get("/{id}", response_model=ItemPublic)
Expand Down
3 changes: 2 additions & 1 deletion backend/app/api/routes/users.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,7 +47,8 @@ def read_users(session: SessionDep, skip: int = 0, limit: int = 100) -> Any:
)
users = session.exec(statement).all()

return UsersPublic(data=users, count=count)
users_public = [UserPublic.model_validate(user) for user in users]
return UsersPublic(data=users_public, count=count)


@router.post(
Expand Down
4 changes: 2 additions & 2 deletions backend/app/models.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -31,7 +31,7 @@ class UserRegister(SQLModel):

# Properties to receive via API on update, all are optional
class UserUpdate(UserBase):
email: EmailStr | None = Field(default=None, max_length=255) # type: ignore
email: EmailStr | None = Field(default=None, max_length=255) # type: ignore[assignment]
password: str | None = Field(default=None, min_length=8, max_length=128)


Expand DownExpand Up@@ -80,7 +80,7 @@ class ItemCreate(ItemBase):

# Properties to receive on item update
class ItemUpdate(ItemBase):
title: str | None = Field(default=None, min_length=1, max_length=255) # type: ignore
title: str | None = Field(default=None, min_length=1, max_length=255) # type: ignore[assignment]


# Database model, database table inferred from class name
Expand Down
2 changes: 1 addition & 1 deletion backend/app/utils.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,7 +4,7 @@
from pathlib import Path
from typing import Any

import emails # type: ignore
import emails # type: ignore[import-untyped]
import jwt
from jinja2 import Template
from jwt.exceptions import InvalidTokenError
Expand Down
4 changes: 4 additions & 0 deletions backend/pyproject.toml
Original file line numberDiff line numberDiff line change
Expand Up@@ -25,6 +25,7 @@ dependencies = [
dev = [
"pytest<8.0.0,>=7.4.3",
"mypy<2.0.0,>=1.8.0",
"ty>=0.0.25",
"ruff<1.0.0,>=0.2.2",
"prek>=0.2.24,<1.0.0",
"coverage<8.0.0,>=7.4.3",
Expand DownExpand Up@@ -75,3 +76,6 @@ sort = "-Cover"

[tool.coverage.html]
show_contexts = true

[tool.ty.terminal]
error-on-warning = true
1 change: 1 addition & 0 deletions backend/scripts/lint.sh
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,5 +4,6 @@ set -e
set -x

mypy app
ty check app
ruff check app
ruff format app --check
26 changes: 26 additions & 0 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading