switch conversion_id to use KSUIDs
Docker server image / build-and-push (push) Successful in 2m47s

This commit is contained in:
2026-03-29 18:04:40 -07:00
parent 2b6fcc478c
commit 15d2997f65
5 changed files with 60 additions and 20 deletions
@@ -260,6 +260,7 @@ func (x *Slide) GetImageUrl() string {
// SlideDeck is the final structured conversion artifact.
type SlideDeck struct {
state protoimpl.MessageState `protogen:"open.v1"`
// Session identifier: KSUID in standard base62 text form. Well-formed values are at most 27 characters (see https://github.com/segmentio/ksuid).
ConversionId string `protobuf:"bytes,1,opt,name=conversion_id,json=conversionId,proto3" json:"conversion_id,omitempty"`
SourceFilename string `protobuf:"bytes,2,opt,name=source_filename,json=sourceFilename,proto3" json:"source_filename,omitempty"`
Slides []*Slide `protobuf:"bytes,3,rep,name=slides,proto3" json:"slides,omitempty"`
@@ -398,6 +399,7 @@ func (x *CreateConversionRequest) GetResolution() ConversionResolution {
// CreateConversionResponse returns upload details for the session.
type CreateConversionResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
// Session identifier: KSUID in standard base62 text form. Well-formed values are at most 27 characters (see https://github.com/segmentio/ksuid).
ConversionId string `protobuf:"bytes,1,opt,name=conversion_id,json=conversionId,proto3" json:"conversion_id,omitempty"`
UploadBucket string `protobuf:"bytes,2,opt,name=upload_bucket,json=uploadBucket,proto3" json:"upload_bucket,omitempty"`
UploadObjectKey string `protobuf:"bytes,3,opt,name=upload_object_key,json=uploadObjectKey,proto3" json:"upload_object_key,omitempty"`
@@ -475,6 +477,7 @@ func (x *CreateConversionResponse) GetExpiresAt() *timestamppb.Timestamp {
// StartConversionRequest requests conversion of an already uploaded PPTX.
type StartConversionRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
// Session identifier: KSUID in standard base62 text form. Well-formed values are at most 27 characters (see https://github.com/segmentio/ksuid).
ConversionId string `protobuf:"bytes,1,opt,name=conversion_id,json=conversionId,proto3" json:"conversion_id,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
@@ -520,6 +523,7 @@ func (x *StartConversionRequest) GetConversionId() string {
// StartConversionResponse returns the first known status after enqueue.
type StartConversionResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
// Session identifier: KSUID in standard base62 text form. Well-formed values are at most 27 characters (see https://github.com/segmentio/ksuid).
ConversionId string `protobuf:"bytes,1,opt,name=conversion_id,json=conversionId,proto3" json:"conversion_id,omitempty"`
Status ConversionStatus `protobuf:"varint,2,opt,name=status,proto3,enum=officeconvertapi.v1.ConversionStatus" json:"status,omitempty"`
unknownFields protoimpl.UnknownFields
@@ -573,6 +577,7 @@ func (x *StartConversionResponse) GetStatus() ConversionStatus {
// GetConversionStatusRequest asks for a specific conversion status.
type GetConversionStatusRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
// Session identifier: KSUID in standard base62 text form. Well-formed values are at most 27 characters (see https://github.com/segmentio/ksuid).
ConversionId string `protobuf:"bytes,1,opt,name=conversion_id,json=conversionId,proto3" json:"conversion_id,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
@@ -618,6 +623,7 @@ func (x *GetConversionStatusRequest) GetConversionId() string {
// GetConversionStatusResponse returns current status and optional error info.
type GetConversionStatusResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
// Session identifier: KSUID in standard base62 text form. Well-formed values are at most 27 characters (see https://github.com/segmentio/ksuid).
ConversionId string `protobuf:"bytes,1,opt,name=conversion_id,json=conversionId,proto3" json:"conversion_id,omitempty"`
Status ConversionStatus `protobuf:"varint,2,opt,name=status,proto3,enum=officeconvertapi.v1.ConversionStatus" json:"status,omitempty"`
ErrorMessage string `protobuf:"bytes,3,opt,name=error_message,json=errorMessage,proto3" json:"error_message,omitempty"`
@@ -711,6 +717,7 @@ func (x *GetConversionStatusResponse) GetMaxProgress() int32 {
// GetSlideDeckRequest fetches a completed deck.
type GetSlideDeckRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
// Session identifier: KSUID in standard base62 text form. Well-formed values are at most 27 characters (see https://github.com/segmentio/ksuid).
ConversionId string `protobuf:"bytes,1,opt,name=conversion_id,json=conversionId,proto3" json:"conversion_id,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
@@ -801,6 +808,7 @@ func (x *GetSlideDeckResponse) GetSlideDeck() *SlideDeck {
// DeleteConversionRequest requests immediate cleanup.
type DeleteConversionRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
// Session identifier: KSUID in standard base62 text form. Well-formed values are at most 27 characters (see https://github.com/segmentio/ksuid).
ConversionId string `protobuf:"bytes,1,opt,name=conversion_id,json=conversionId,proto3" json:"conversion_id,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
@@ -846,6 +854,7 @@ func (x *DeleteConversionRequest) GetConversionId() string {
// DeleteConversionResponse confirms cleanup details.
type DeleteConversionResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
// Session identifier: KSUID in standard base62 text form. Well-formed values are at most 27 characters (see https://github.com/segmentio/ksuid).
ConversionId string `protobuf:"bytes,1,opt,name=conversion_id,json=conversionId,proto3" json:"conversion_id,omitempty"`
Deleted bool `protobuf:"varint,2,opt,name=deleted,proto3" json:"deleted,omitempty"`
unknownFields protoimpl.UnknownFields
@@ -62,6 +62,7 @@ message Slide {
// SlideDeck is the final structured conversion artifact.
message SlideDeck {
// Session identifier: KSUID in standard base62 text form. Well-formed values are at most 27 characters (see https://github.com/segmentio/ksuid).
string conversion_id = 1;
string source_filename = 2;
repeated Slide slides = 3;
@@ -78,6 +79,7 @@ message CreateConversionRequest {
// CreateConversionResponse returns upload details for the session.
message CreateConversionResponse {
// Session identifier: KSUID in standard base62 text form. Well-formed values are at most 27 characters (see https://github.com/segmentio/ksuid).
string conversion_id = 1;
string upload_bucket = 2;
string upload_object_key = 3;
@@ -87,22 +89,26 @@ message CreateConversionResponse {
// StartConversionRequest requests conversion of an already uploaded PPTX.
message StartConversionRequest {
// Session identifier: KSUID in standard base62 text form. Well-formed values are at most 27 characters (see https://github.com/segmentio/ksuid).
string conversion_id = 1;
}
// StartConversionResponse returns the first known status after enqueue.
message StartConversionResponse {
// Session identifier: KSUID in standard base62 text form. Well-formed values are at most 27 characters (see https://github.com/segmentio/ksuid).
string conversion_id = 1;
ConversionStatus status = 2;
}
// GetConversionStatusRequest asks for a specific conversion status.
message GetConversionStatusRequest {
// Session identifier: KSUID in standard base62 text form. Well-formed values are at most 27 characters (see https://github.com/segmentio/ksuid).
string conversion_id = 1;
}
// GetConversionStatusResponse returns current status and optional error info.
message GetConversionStatusResponse {
// Session identifier: KSUID in standard base62 text form. Well-formed values are at most 27 characters (see https://github.com/segmentio/ksuid).
string conversion_id = 1;
ConversionStatus status = 2;
string error_message = 3;
@@ -114,6 +120,7 @@ message GetConversionStatusResponse {
// GetSlideDeckRequest fetches a completed deck.
message GetSlideDeckRequest {
// Session identifier: KSUID in standard base62 text form. Well-formed values are at most 27 characters (see https://github.com/segmentio/ksuid).
string conversion_id = 1;
}
@@ -124,11 +131,13 @@ message GetSlideDeckResponse {
// DeleteConversionRequest requests immediate cleanup.
message DeleteConversionRequest {
// Session identifier: KSUID in standard base62 text form. Well-formed values are at most 27 characters (see https://github.com/segmentio/ksuid).
string conversion_id = 1;
}
// DeleteConversionResponse confirms cleanup details.
message DeleteConversionResponse {
// Session identifier: KSUID in standard base62 text form. Well-formed values are at most 27 characters (see https://github.com/segmentio/ksuid).
string conversion_id = 1;
bool deleted = 2;
}
+1
View File
@@ -8,6 +8,7 @@ dependencies = [
"connectrpc>=0.6.0",
"minio>=7.2.18",
"officeconvert",
"svix-ksuid>=0.7.0",
"uvicorn>=0.35.0",
]
@@ -10,7 +10,6 @@ from pathlib import Path
import shutil
import tempfile
import time
import uuid
from connectrpc.code import Code
from connectrpc.errors import ConnectError
@@ -29,6 +28,7 @@ from officeconvert.conversion import (
RESOLUTION_UHD,
)
from officeconvertapi.v1 import conversion_connect, conversion_pb2
from ksuid import Ksuid
from minio.error import S3Error
@@ -75,8 +75,9 @@ class ConversionServiceImpl(conversion_connect.ConversionService):
if resolution not in _RESOLUTION_PRESET_BY_PROTO:
raise ConnectError(Code.INVALID_ARGUMENT, "resolution is invalid")
conversion_id = str(uuid.uuid4())
bucket_name = f"oc-{conversion_id}"
ksuid = Ksuid()
conversion_id = str(ksuid)
bucket_name = f"oc-{bytes(ksuid).hex()}"
upload_key = "input/source.pptx"
expires_at = utc_now() + timedelta(seconds=self._config.s3_session_ttl_seconds)
+20
View File
@@ -292,6 +292,7 @@ dependencies = [
{ name = "connectrpc" },
{ name = "minio" },
{ name = "officeconvert" },
{ name = "svix-ksuid" },
{ name = "uvicorn" },
]
@@ -300,6 +301,7 @@ requires-dist = [
{ name = "connectrpc", specifier = ">=0.6.0" },
{ name = "minio", specifier = ">=7.2.18" },
{ name = "officeconvert", editable = "packages/officeconvert" },
{ name = "svix-ksuid", specifier = ">=0.7.0" },
{ name = "uvicorn", specifier = ">=0.35.0" },
]
@@ -479,6 +481,12 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/2d/d8/d6710bbb38f6a715135f7c8a8e5c6227d69299a2b7e989c81315a08054e7/pyqwest-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:a7b8d8ae51ccf6375a9e82e5b38d2129ee3121acf4933a37e541f4fe04a5f758", size = 4577924, upload-time = "2026-03-06T02:32:31.013Z" },
]
[[package]]
name = "python-baseconv"
version = "1.2.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/33/d0/9297d7d8dd74767b4d5560d834b30b2fff17d39987c23ed8656f476e0d9b/python-baseconv-1.2.2.tar.gz", hash = "sha256:0539f8bd0464013b05ad62e0a1673f0ac9086c76b43ebf9f833053527cd9931b", size = 4929, upload-time = "2019-04-04T19:28:57.17Z" }
[[package]]
name = "python-pptx"
version = "1.0.2"
@@ -494,6 +502,18 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/d9/4f/00be2196329ebbff56ce564aa94efb0fbc828d00de250b1980de1a34ab49/python_pptx-1.0.2-py3-none-any.whl", hash = "sha256:160838e0b8565a8b1f67947675886e9fea18aa5e795db7ae531606d68e785cba", size = 472788, upload-time = "2024-08-07T17:33:28.192Z" },
]
[[package]]
name = "svix-ksuid"
version = "0.7.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "python-baseconv" },
]
sdist = { url = "https://files.pythonhosted.org/packages/ef/f0/b534cc208ee8ea010254208345324d2e3d3c5d13a210d2249f9f11840f2f/svix_ksuid-0.7.0.tar.gz", hash = "sha256:e6b824437b5bb76d75163b928cbf9ae6c871d3e713e5de75cde89bae6a19bfe8", size = 5810, upload-time = "2026-03-14T02:44:51.944Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/74/51/922e6ad4da6b3f4863ea059db2391259c9247b9fe4f0c08da50eff4e1312/svix_ksuid-0.7.0-py3-none-any.whl", hash = "sha256:6bceae49f85e026c77a13e6de81bf4536c15039ecb160415e954403618c2a06f", size = 5607, upload-time = "2026-03-14T02:44:50.822Z" },
]
[[package]]
name = "typing-extensions"
version = "4.15.0"