mirror of
https://github.com/suitenumerique/meet.git
synced 2026-10-14 17:10:41 +00:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| eb422309b9 | |||
| 4854fc0c13 | |||
| d930fa1ba6 | |||
| 6f1414b34e | |||
| 0194f7f3f3 | |||
| 0bde103911 |
@@ -22,6 +22,9 @@
|
|||||||
# See http://jorisroovers.github.io/gitlint/user_defined_rules for details
|
# See http://jorisroovers.github.io/gitlint/user_defined_rules for details
|
||||||
extra-path=gitlint/
|
extra-path=gitlint/
|
||||||
|
|
||||||
|
# Use "search" semantics for regexes (gitlint's future default)
|
||||||
|
regex-style-search=True
|
||||||
|
|
||||||
# [title-max-length]
|
# [title-max-length]
|
||||||
# line-length=80
|
# line-length=80
|
||||||
|
|
||||||
|
|||||||
@@ -10,9 +10,16 @@ and this project adheres to
|
|||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
- ⚡️(backend) add UPPER(email) index for case-insensitive user lookups
|
||||||
|
|
||||||
|
## [1.35.0] - 2026-10-13
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
- ✨(frontend) let users pick the recording encoding
|
- ✨(frontend) let users pick the recording encoding
|
||||||
- ✨(backend) introduce a token exchange endpoint for iframe embeds
|
- ✨(backend) introduce a token exchange endpoint for iframe embeds
|
||||||
- ⚡️(backend) send recording notification emails from a Celery task
|
- ⚡️(backend) send recording notification emails from a Celery task
|
||||||
|
- ✨(backend) allow static feature flags without an analytics backend #1809
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
|||||||
+13
-13
@@ -5,17 +5,17 @@ services:
|
|||||||
env_file:
|
env_file:
|
||||||
- env.d/development/postgresql
|
- env.d/development/postgresql
|
||||||
ports:
|
ports:
|
||||||
- "15432:5432"
|
- "127.0.0.1:15432:5432"
|
||||||
|
|
||||||
redis:
|
redis:
|
||||||
image: redis:5
|
image: redis:5
|
||||||
ports:
|
ports:
|
||||||
- "6379:6379"
|
- "127.0.0.1:6379:6379"
|
||||||
|
|
||||||
mailcatcher:
|
mailcatcher:
|
||||||
image: sj26/mailcatcher:latest
|
image: sj26/mailcatcher:latest
|
||||||
ports:
|
ports:
|
||||||
- "1081:1080"
|
- "127.0.0.1:1081:1080"
|
||||||
|
|
||||||
garage:
|
garage:
|
||||||
user: ${DOCKER_USER:-1000}
|
user: ${DOCKER_USER:-1000}
|
||||||
@@ -71,7 +71,7 @@ services:
|
|||||||
- env.d/development/common
|
- env.d/development/common
|
||||||
- env.d/development/postgresql
|
- env.d/development/postgresql
|
||||||
ports:
|
ports:
|
||||||
- "8071:8000"
|
- "127.0.0.1:8071:8000"
|
||||||
volumes:
|
volumes:
|
||||||
- ./src/backend:/app
|
- ./src/backend:/app
|
||||||
- ./data/static:/data/static
|
- ./data/static:/data/static
|
||||||
@@ -137,7 +137,7 @@ services:
|
|||||||
nginx:
|
nginx:
|
||||||
image: nginx:1.25
|
image: nginx:1.25
|
||||||
ports:
|
ports:
|
||||||
- "8083:8083"
|
- "127.0.0.1:8083:8083"
|
||||||
volumes:
|
volumes:
|
||||||
- ./docker/files/etc/nginx/conf.d:/etc/nginx/conf.d:ro
|
- ./docker/files/etc/nginx/conf.d:/etc/nginx/conf.d:ro
|
||||||
depends_on:
|
depends_on:
|
||||||
@@ -159,7 +159,7 @@ services:
|
|||||||
VITE_APP_TITLE: "LaSuite Meet"
|
VITE_APP_TITLE: "LaSuite Meet"
|
||||||
image: meet:frontend-development
|
image: meet:frontend-development
|
||||||
ports:
|
ports:
|
||||||
- "3000:8080"
|
- "127.0.0.1:3000:8080"
|
||||||
|
|
||||||
dockerize:
|
dockerize:
|
||||||
image: jwilder/dockerize
|
image: jwilder/dockerize
|
||||||
@@ -185,7 +185,7 @@ services:
|
|||||||
kc_postgresql:
|
kc_postgresql:
|
||||||
image: postgres:14.3
|
image: postgres:14.3
|
||||||
ports:
|
ports:
|
||||||
- "5433:5432"
|
- "127.0.0.1:5433:5432"
|
||||||
env_file:
|
env_file:
|
||||||
- env.d/development/kc_postgresql
|
- env.d/development/kc_postgresql
|
||||||
|
|
||||||
@@ -213,7 +213,7 @@ services:
|
|||||||
KC_DB_SCHEMA: public
|
KC_DB_SCHEMA: public
|
||||||
PROXY_ADDRESS_FORWARDING: 'true'
|
PROXY_ADDRESS_FORWARDING: 'true'
|
||||||
ports:
|
ports:
|
||||||
- "8080:8080"
|
- "127.0.0.1:8080:8080"
|
||||||
depends_on:
|
depends_on:
|
||||||
- kc_postgresql
|
- kc_postgresql
|
||||||
|
|
||||||
@@ -222,10 +222,10 @@ services:
|
|||||||
entrypoint: /livekit-server --dev --bind 0.0.0.0 --config ./config.yaml
|
entrypoint: /livekit-server --dev --bind 0.0.0.0 --config ./config.yaml
|
||||||
ports:
|
ports:
|
||||||
- "7880:7880"
|
- "7880:7880"
|
||||||
- "7881:7881"
|
- "127.0.0.1:7881:7881"
|
||||||
- "7882:7882/udp"
|
- "127.0.0.1:7882:7882/udp"
|
||||||
- "3478:3478/udp"
|
- "127.0.0.1:3478:3478/udp"
|
||||||
- "30000-30100:30000-30100/udp"
|
- "127.0.0.1:30000-30100:30000-30100/udp"
|
||||||
volumes:
|
volumes:
|
||||||
- ./docker/livekit/config/livekit-server.yaml:/config.yaml
|
- ./docker/livekit/config/livekit-server.yaml:/config.yaml
|
||||||
depends_on:
|
depends_on:
|
||||||
@@ -283,7 +283,7 @@ services:
|
|||||||
env_file:
|
env_file:
|
||||||
- env.d/development/summary
|
- env.d/development/summary
|
||||||
ports:
|
ports:
|
||||||
- "8001:8000"
|
- "127.0.0.1:8001:8000"
|
||||||
volumes:
|
volumes:
|
||||||
- ./src/summary:/app
|
- ./src/summary:/app
|
||||||
depends_on:
|
depends_on:
|
||||||
|
|||||||
@@ -45,4 +45,4 @@ services:
|
|||||||
# The local proxy listens on 8080 and collides with Keycloak's published admin port.
|
# The local proxy listens on 8080 and collides with Keycloak's published admin port.
|
||||||
keycloak:
|
keycloak:
|
||||||
ports: !override
|
ports: !override
|
||||||
- "8081:8080"
|
- "127.0.0.1:8081:8080"
|
||||||
|
|||||||
@@ -120,6 +120,26 @@ DJANGO_EMAIL_BRAND_NAME=<brand name used in email templates> # e.g. "La Suite Nu
|
|||||||
DJANGO_EMAIL_LOGO_IMG=<logo image to use in email templates.> # e.g. "https://meet.yourdomain.tld/assets/logo-suite-numerique.png"
|
DJANGO_EMAIL_LOGO_IMG=<logo image to use in email templates.> # e.g. "https://meet.yourdomain.tld/assets/logo-suite-numerique.png"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Feature flags without an analytics backend
|
||||||
|
|
||||||
|
Some features, such as automatic meeting summaries (`summary-enabled`), are gated by a feature flag read from the analytics backend. You do not need an analytics backend to enable them: leave `ANALYTICS_BACKEND` unset and declare the flags in `env.d/common`. No data is sent anywhere.
|
||||||
|
|
||||||
|
```env
|
||||||
|
ANALYTICS_BACKEND_SETTINGS={"feature_flags": {"summary-enabled": True}}
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also set it directly in `compose.yaml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
backend:
|
||||||
|
environment:
|
||||||
|
ANALYTICS_BACKEND_SETTINGS: '{"feature_flags": {"summary-enabled": True}}'
|
||||||
|
```
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> The value is a Python dictionary, not JSON: write `True`, not `true`. Set it on every container running the backend image.
|
||||||
|
|
||||||
## Step 3: Configure your firewall
|
## Step 3: Configure your firewall
|
||||||
|
|
||||||
If you are using a firewall as it is usually recommended in a production environment you will need to allow the webservice traffic on ports 80 and 443 but also to allow UDP traffic for the WebRTC service.
|
If you are using a firewall as it is usually recommended in a production environment you will need to allow the webservice traffic on ports 80 and 443 but also to allow UDP traffic for the WebRTC service.
|
||||||
|
|||||||
@@ -416,6 +416,8 @@ These are the environmental options available on meet backend.
|
|||||||
| SCREEN_RECORDING_BASE_URL | Screen recording base URL | |
|
| SCREEN_RECORDING_BASE_URL | Screen recording base URL | |
|
||||||
| SUMMARY_SERVICE_ENDPOINT | Summary service endpoint | |
|
| SUMMARY_SERVICE_ENDPOINT | Summary service endpoint | |
|
||||||
| SUMMARY_SERVICE_API_TOKEN | API token for summary service | |
|
| SUMMARY_SERVICE_API_TOKEN | API token for summary service | |
|
||||||
|
| ANALYTICS_BACKEND | Analytics backend class, e.g. `core.analytics.posthog.PostHogAnalytics`. Nothing is sent when unset | |
|
||||||
|
| ANALYTICS_BACKEND_SETTINGS | Analytics backend settings. Without a backend, `{"feature_flags": {"summary-enabled": True}}` sets feature flags statically | {} |
|
||||||
| SIGNUP_NEW_USER_TO_MARKETING_EMAIL | Signup users to marketing emails | false |
|
| SIGNUP_NEW_USER_TO_MARKETING_EMAIL | Signup users to marketing emails | false |
|
||||||
| MARKETING_SERVICE_CLASS | Marketing service class | core.services.marketing.BrevoMarketingService |
|
| MARKETING_SERVICE_CLASS | Marketing service class | core.services.marketing.BrevoMarketingService |
|
||||||
| BREVO_API_KEY | Brevo API key for marketing emails | |
|
| BREVO_API_KEY | Brevo API key for marketing emails | |
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "agents"
|
name = "agents"
|
||||||
version = "1.34.0"
|
version = "1.35.0"
|
||||||
requires-python = ">=3.12"
|
requires-python = ">=3.12"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"livekit-agents==1.7.0",
|
"livekit-agents==1.7.0",
|
||||||
|
|||||||
Generated
+1
-1
@@ -9,7 +9,7 @@ resolution-markers = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "agents"
|
name = "agents"
|
||||||
version = "1.34.0"
|
version = "1.35.0"
|
||||||
source = { virtual = "." }
|
source = { virtual = "." }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "boto3" },
|
{ name = "boto3" },
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ def get_analytics() -> AnalyticsBackend:
|
|||||||
options = getattr(settings, "ANALYTICS_BACKEND_SETTINGS", {}) or {}
|
options = getattr(settings, "ANALYTICS_BACKEND_SETTINGS", {}) or {}
|
||||||
|
|
||||||
if not dotted_path:
|
if not dotted_path:
|
||||||
return NoOpAnalytics()
|
return NoOpAnalytics(**options)
|
||||||
|
|
||||||
backend_class = import_string(dotted_path)
|
backend_class = import_string(dotted_path)
|
||||||
return backend_class(**options)
|
return backend_class(**options)
|
||||||
|
|||||||
@@ -53,7 +53,23 @@ class AnalyticsBackend(ABC):
|
|||||||
|
|
||||||
|
|
||||||
class NoOpAnalytics(AnalyticsBackend):
|
class NoOpAnalytics(AnalyticsBackend):
|
||||||
"""Default backend: silently discards everything."""
|
"""
|
||||||
|
Default backend: discards events and serves static feature flags.
|
||||||
|
|
||||||
|
Feature flags can be declared without any analytics provider, e.g.:
|
||||||
|
|
||||||
|
ANALYTICS_BACKEND_SETTINGS = {"feature_flags": {"summary-enabled": True}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
feature_flags: Mapping[str, bool | str | None] | None = None,
|
||||||
|
**_kwargs: Any,
|
||||||
|
) -> None:
|
||||||
|
# Other settings are ignored, so that settings meant for another
|
||||||
|
# backend never break an instance running without one.
|
||||||
|
self._feature_flags = dict(feature_flags or {})
|
||||||
|
|
||||||
def identify(self, user: User, properties=None) -> None:
|
def identify(self, user: User, properties=None) -> None:
|
||||||
"""No-op: discards identify calls."""
|
"""No-op: discards identify calls."""
|
||||||
@@ -63,3 +79,10 @@ class NoOpAnalytics(AnalyticsBackend):
|
|||||||
|
|
||||||
def shutdown(self) -> None:
|
def shutdown(self) -> None:
|
||||||
"""No-op: nothing to flush."""
|
"""No-op: nothing to flush."""
|
||||||
|
|
||||||
|
def get_user_feature_flags(
|
||||||
|
self,
|
||||||
|
user: User,
|
||||||
|
) -> Mapping[UserFeatureFlag, bool | str | None]:
|
||||||
|
"""Return the feature flags declared in settings, for any user."""
|
||||||
|
return self._feature_flags
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
# Generated by Django 5.2.14 on 2026-10-07 18:45
|
||||||
|
|
||||||
|
from django.contrib.postgres.operations import AddIndexConcurrently
|
||||||
|
from django.db import migrations, models
|
||||||
|
from django.db.models.functions import Upper
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
atomic = False
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('core', '0026_alter_application_scopes'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
AddIndexConcurrently(
|
||||||
|
model_name="user",
|
||||||
|
index=models.Index(
|
||||||
|
Upper("email"),
|
||||||
|
name="user_email_upper_idx",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -246,6 +246,9 @@ class User(AbstractBaseUser, BaseModel, auth_models.PermissionsMixin):
|
|||||||
name="unique_email_when_sub_is_null",
|
name="unique_email_when_sub_is_null",
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
indexes = [
|
||||||
|
models.Index(models.functions.Upper("email"), name="user_email_upper_idx"),
|
||||||
|
]
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.email or self.admin_email or str(self.id)
|
return self.email or self.admin_email or str(self.id)
|
||||||
|
|||||||
@@ -0,0 +1,124 @@
|
|||||||
|
"""
|
||||||
|
Unit tests for NoOpAnalytics and the analytics backend loader.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# pylint: disable=redefined-outer-name,unused-argument
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from core.analytics import get_analytics
|
||||||
|
from core.analytics.base import NoOpAnalytics
|
||||||
|
from core.analytics.user_feature_flags import UserFeatureFlag
|
||||||
|
from core.factories import UserFactory
|
||||||
|
|
||||||
|
pytestmark = pytest.mark.django_db
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def clear_analytics_cache():
|
||||||
|
"""Clear the cached backend before and after each test."""
|
||||||
|
get_analytics.cache_clear()
|
||||||
|
yield
|
||||||
|
get_analytics.cache_clear()
|
||||||
|
|
||||||
|
|
||||||
|
# ==============================
|
||||||
|
# NoOpAnalytics
|
||||||
|
# ==============================
|
||||||
|
|
||||||
|
|
||||||
|
def test_noop_has_no_feature_flags_by_default():
|
||||||
|
"""Should keep every feature flag disabled when none is declared."""
|
||||||
|
backend = NoOpAnalytics()
|
||||||
|
user = UserFactory()
|
||||||
|
|
||||||
|
assert not backend.get_user_feature_flags(user)
|
||||||
|
assert (
|
||||||
|
backend.is_user_feature_enabled(
|
||||||
|
user, UserFeatureFlag.TRANSCRIPT_SUMMARY_ENABLED
|
||||||
|
)
|
||||||
|
is False
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_noop_enables_declared_feature_flags():
|
||||||
|
"""Should enable a feature flag declared as True."""
|
||||||
|
backend = NoOpAnalytics(feature_flags={"summary-enabled": True})
|
||||||
|
user = UserFactory()
|
||||||
|
|
||||||
|
assert backend.get_user_feature_flags(user) == {"summary-enabled": True}
|
||||||
|
assert (
|
||||||
|
backend.is_user_feature_enabled(
|
||||||
|
user, UserFeatureFlag.TRANSCRIPT_SUMMARY_ENABLED
|
||||||
|
)
|
||||||
|
is True
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("value", [False, None, "true"])
|
||||||
|
def test_noop_only_enables_feature_flags_set_to_true(value):
|
||||||
|
"""Should keep a feature flag disabled unless its value is exactly True."""
|
||||||
|
backend = NoOpAnalytics(feature_flags={"summary-enabled": value})
|
||||||
|
user = UserFactory()
|
||||||
|
|
||||||
|
assert (
|
||||||
|
backend.is_user_feature_enabled(
|
||||||
|
user, UserFeatureFlag.TRANSCRIPT_SUMMARY_ENABLED
|
||||||
|
)
|
||||||
|
is False
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_noop_ignores_unrelated_settings():
|
||||||
|
"""Should accept settings meant for another backend without failing."""
|
||||||
|
backend = NoOpAnalytics(api_key="my-key", host="https://eu.i.posthog.com")
|
||||||
|
|
||||||
|
assert not backend.get_user_feature_flags(UserFactory())
|
||||||
|
|
||||||
|
|
||||||
|
# ==============================
|
||||||
|
# get_analytics
|
||||||
|
# ==============================
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_analytics_defaults_to_noop_without_feature_flags(settings):
|
||||||
|
"""Should return a no-op backend with no feature flag by default."""
|
||||||
|
settings.ANALYTICS_BACKEND = None
|
||||||
|
settings.ANALYTICS_BACKEND_SETTINGS = {}
|
||||||
|
|
||||||
|
backend = get_analytics()
|
||||||
|
|
||||||
|
assert isinstance(backend, NoOpAnalytics)
|
||||||
|
assert not backend.get_user_feature_flags(UserFactory())
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_analytics_passes_feature_flags_to_noop(settings):
|
||||||
|
"""Should forward the feature flags when no backend is configured."""
|
||||||
|
settings.ANALYTICS_BACKEND = None
|
||||||
|
settings.ANALYTICS_BACKEND_SETTINGS = {"feature_flags": {"summary-enabled": True}}
|
||||||
|
|
||||||
|
backend = get_analytics()
|
||||||
|
|
||||||
|
assert isinstance(backend, NoOpAnalytics)
|
||||||
|
assert (
|
||||||
|
backend.is_user_feature_enabled(
|
||||||
|
UserFactory(), UserFeatureFlag.TRANSCRIPT_SUMMARY_ENABLED
|
||||||
|
)
|
||||||
|
is True
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_analytics_instantiates_configured_backend_with_settings(settings):
|
||||||
|
"""Should instantiate the configured backend with its settings."""
|
||||||
|
settings.ANALYTICS_BACKEND = "core.analytics.base.NoOpAnalytics"
|
||||||
|
settings.ANALYTICS_BACKEND_SETTINGS = {"feature_flags": {"summary-enabled": True}}
|
||||||
|
|
||||||
|
backend = get_analytics()
|
||||||
|
|
||||||
|
assert isinstance(backend, NoOpAnalytics)
|
||||||
|
assert (
|
||||||
|
backend.is_user_feature_enabled(
|
||||||
|
UserFactory(), UserFeatureFlag.TRANSCRIPT_SUMMARY_ENABLED
|
||||||
|
)
|
||||||
|
is True
|
||||||
|
)
|
||||||
@@ -7,7 +7,7 @@ build-backend = "uv_build"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "meet"
|
name = "meet"
|
||||||
version = "1.34.0"
|
version = "1.35.0"
|
||||||
authors = [{ "name" = "DINUM", "email" = "dev@mail.numerique.gouv.fr" }]
|
authors = [{ "name" = "DINUM", "email" = "dev@mail.numerique.gouv.fr" }]
|
||||||
classifiers = [
|
classifiers = [
|
||||||
"Development Status :: 5 - Production/Stable",
|
"Development Status :: 5 - Production/Stable",
|
||||||
|
|||||||
Generated
+1
-1
@@ -1297,7 +1297,7 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "meet"
|
name = "meet"
|
||||||
version = "1.34.0"
|
version = "1.35.0"
|
||||||
source = { editable = "." }
|
source = { editable = "." }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "aiohttp" },
|
{ name = "aiohttp" },
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "meet",
|
"name": "meet",
|
||||||
"version": "1.34.0",
|
"version": "1.35.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "meet",
|
"name": "meet",
|
||||||
"version": "1.34.0",
|
"version": "1.35.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fontsource-variable/atkinson-hyperlegible-next": "5.3.0",
|
"@fontsource-variable/atkinson-hyperlegible-next": "5.3.0",
|
||||||
"@fontsource-variable/lexend": "5.3.0",
|
"@fontsource-variable/lexend": "5.3.0",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "meet",
|
"name": "meet",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.34.0",
|
"version": "1.35.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "panda codegen && vite",
|
"dev": "panda codegen && vite",
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "mail_mjml",
|
"name": "mail_mjml",
|
||||||
"version": "1.34.0",
|
"version": "1.35.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "mail_mjml",
|
"name": "mail_mjml",
|
||||||
"version": "1.34.0",
|
"version": "1.35.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@html-to/text-cli": "0.6.1",
|
"@html-to/text-cli": "0.6.1",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "mail_mjml",
|
"name": "mail_mjml",
|
||||||
"version": "1.34.0",
|
"version": "1.35.0",
|
||||||
"description": "An util to generate html and text django's templates from mjml templates",
|
"description": "An util to generate html and text django's templates from mjml templates",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "sdk",
|
"name": "sdk",
|
||||||
"version": "1.34.0",
|
"version": "1.35.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "sdk",
|
"name": "sdk",
|
||||||
"version": "1.34.0",
|
"version": "1.35.0",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"./library",
|
"./library",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "sdk",
|
"name": "sdk",
|
||||||
"version": "1.34.0",
|
"version": "1.35.0",
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"description": "",
|
"description": "",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "summary"
|
name = "summary"
|
||||||
version = "1.34.0"
|
version = "1.35.0"
|
||||||
requires-python = ">=3.13"
|
requires-python = ">=3.13"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fastapi[standard]>=0.105.0",
|
"fastapi[standard]>=0.105.0",
|
||||||
|
|||||||
Generated
+1
-1
@@ -1484,7 +1484,7 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "summary"
|
name = "summary"
|
||||||
version = "1.34.0"
|
version = "1.35.0"
|
||||||
source = { editable = "." }
|
source = { editable = "." }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "boto3" },
|
{ name = "boto3" },
|
||||||
|
|||||||
Reference in New Issue
Block a user