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
+26 -17
View File
@@ -259,7 +259,8 @@ func (x *Slide) GetImageUrl() string {
// SlideDeck is the final structured conversion artifact. // SlideDeck is the final structured conversion artifact.
type SlideDeck struct { type SlideDeck struct {
state protoimpl.MessageState `protogen:"open.v1"` 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"` 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"` 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"` Slides []*Slide `protobuf:"bytes,3,rep,name=slides,proto3" json:"slides,omitempty"`
@@ -397,7 +398,8 @@ func (x *CreateConversionRequest) GetResolution() ConversionResolution {
// CreateConversionResponse returns upload details for the session. // CreateConversionResponse returns upload details for the session.
type CreateConversionResponse struct { type CreateConversionResponse struct {
state protoimpl.MessageState `protogen:"open.v1"` 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"` 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"` 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"` UploadObjectKey string `protobuf:"bytes,3,opt,name=upload_object_key,json=uploadObjectKey,proto3" json:"upload_object_key,omitempty"`
@@ -474,8 +476,9 @@ func (x *CreateConversionResponse) GetExpiresAt() *timestamppb.Timestamp {
// StartConversionRequest requests conversion of an already uploaded PPTX. // StartConversionRequest requests conversion of an already uploaded PPTX.
type StartConversionRequest struct { type StartConversionRequest struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState `protogen:"open.v1"`
ConversionId string `protobuf:"bytes,1,opt,name=conversion_id,json=conversionId,proto3" json:"conversion_id,omitempty"` // 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 unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
} }
@@ -519,9 +522,10 @@ func (x *StartConversionRequest) GetConversionId() string {
// StartConversionResponse returns the first known status after enqueue. // StartConversionResponse returns the first known status after enqueue.
type StartConversionResponse struct { type StartConversionResponse struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState `protogen:"open.v1"`
ConversionId string `protobuf:"bytes,1,opt,name=conversion_id,json=conversionId,proto3" json:"conversion_id,omitempty"` // Session identifier: KSUID in standard base62 text form. Well-formed values are at most 27 characters (see https://github.com/segmentio/ksuid).
Status ConversionStatus `protobuf:"varint,2,opt,name=status,proto3,enum=officeconvertapi.v1.ConversionStatus" json:"status,omitempty"` 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 unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
} }
@@ -572,8 +576,9 @@ func (x *StartConversionResponse) GetStatus() ConversionStatus {
// GetConversionStatusRequest asks for a specific conversion status. // GetConversionStatusRequest asks for a specific conversion status.
type GetConversionStatusRequest struct { type GetConversionStatusRequest struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState `protogen:"open.v1"`
ConversionId string `protobuf:"bytes,1,opt,name=conversion_id,json=conversionId,proto3" json:"conversion_id,omitempty"` // 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 unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
} }
@@ -617,7 +622,8 @@ func (x *GetConversionStatusRequest) GetConversionId() string {
// GetConversionStatusResponse returns current status and optional error info. // GetConversionStatusResponse returns current status and optional error info.
type GetConversionStatusResponse struct { type GetConversionStatusResponse struct {
state protoimpl.MessageState `protogen:"open.v1"` 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"` 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"` 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"` ErrorMessage string `protobuf:"bytes,3,opt,name=error_message,json=errorMessage,proto3" json:"error_message,omitempty"`
@@ -710,8 +716,9 @@ func (x *GetConversionStatusResponse) GetMaxProgress() int32 {
// GetSlideDeckRequest fetches a completed deck. // GetSlideDeckRequest fetches a completed deck.
type GetSlideDeckRequest struct { type GetSlideDeckRequest struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState `protogen:"open.v1"`
ConversionId string `protobuf:"bytes,1,opt,name=conversion_id,json=conversionId,proto3" json:"conversion_id,omitempty"` // 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 unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
} }
@@ -800,8 +807,9 @@ func (x *GetSlideDeckResponse) GetSlideDeck() *SlideDeck {
// DeleteConversionRequest requests immediate cleanup. // DeleteConversionRequest requests immediate cleanup.
type DeleteConversionRequest struct { type DeleteConversionRequest struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState `protogen:"open.v1"`
ConversionId string `protobuf:"bytes,1,opt,name=conversion_id,json=conversionId,proto3" json:"conversion_id,omitempty"` // 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 unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
} }
@@ -845,9 +853,10 @@ func (x *DeleteConversionRequest) GetConversionId() string {
// DeleteConversionResponse confirms cleanup details. // DeleteConversionResponse confirms cleanup details.
type DeleteConversionResponse struct { type DeleteConversionResponse struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState `protogen:"open.v1"`
ConversionId string `protobuf:"bytes,1,opt,name=conversion_id,json=conversionId,proto3" json:"conversion_id,omitempty"` // Session identifier: KSUID in standard base62 text form. Well-formed values are at most 27 characters (see https://github.com/segmentio/ksuid).
Deleted bool `protobuf:"varint,2,opt,name=deleted,proto3" json:"deleted,omitempty"` 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 unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
} }
@@ -62,6 +62,7 @@ message Slide {
// SlideDeck is the final structured conversion artifact. // SlideDeck is the final structured conversion artifact.
message SlideDeck { 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 conversion_id = 1;
string source_filename = 2; string source_filename = 2;
repeated Slide slides = 3; repeated Slide slides = 3;
@@ -78,6 +79,7 @@ message CreateConversionRequest {
// CreateConversionResponse returns upload details for the session. // CreateConversionResponse returns upload details for the session.
message CreateConversionResponse { 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 conversion_id = 1;
string upload_bucket = 2; string upload_bucket = 2;
string upload_object_key = 3; string upload_object_key = 3;
@@ -87,22 +89,26 @@ message CreateConversionResponse {
// StartConversionRequest requests conversion of an already uploaded PPTX. // StartConversionRequest requests conversion of an already uploaded PPTX.
message StartConversionRequest { 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; string conversion_id = 1;
} }
// StartConversionResponse returns the first known status after enqueue. // StartConversionResponse returns the first known status after enqueue.
message StartConversionResponse { 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; string conversion_id = 1;
ConversionStatus status = 2; ConversionStatus status = 2;
} }
// GetConversionStatusRequest asks for a specific conversion status. // GetConversionStatusRequest asks for a specific conversion status.
message GetConversionStatusRequest { 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; string conversion_id = 1;
} }
// GetConversionStatusResponse returns current status and optional error info. // GetConversionStatusResponse returns current status and optional error info.
message GetConversionStatusResponse { 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; string conversion_id = 1;
ConversionStatus status = 2; ConversionStatus status = 2;
string error_message = 3; string error_message = 3;
@@ -114,6 +120,7 @@ message GetConversionStatusResponse {
// GetSlideDeckRequest fetches a completed deck. // GetSlideDeckRequest fetches a completed deck.
message GetSlideDeckRequest { 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; string conversion_id = 1;
} }
@@ -124,11 +131,13 @@ message GetSlideDeckResponse {
// DeleteConversionRequest requests immediate cleanup. // DeleteConversionRequest requests immediate cleanup.
message DeleteConversionRequest { 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; string conversion_id = 1;
} }
// DeleteConversionResponse confirms cleanup details. // DeleteConversionResponse confirms cleanup details.
message DeleteConversionResponse { 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; string conversion_id = 1;
bool deleted = 2; bool deleted = 2;
} }
+1
View File
@@ -8,6 +8,7 @@ dependencies = [
"connectrpc>=0.6.0", "connectrpc>=0.6.0",
"minio>=7.2.18", "minio>=7.2.18",
"officeconvert", "officeconvert",
"svix-ksuid>=0.7.0",
"uvicorn>=0.35.0", "uvicorn>=0.35.0",
] ]
@@ -10,7 +10,6 @@ from pathlib import Path
import shutil import shutil
import tempfile import tempfile
import time import time
import uuid
from connectrpc.code import Code from connectrpc.code import Code
from connectrpc.errors import ConnectError from connectrpc.errors import ConnectError
@@ -29,6 +28,7 @@ from officeconvert.conversion import (
RESOLUTION_UHD, RESOLUTION_UHD,
) )
from officeconvertapi.v1 import conversion_connect, conversion_pb2 from officeconvertapi.v1 import conversion_connect, conversion_pb2
from ksuid import Ksuid
from minio.error import S3Error from minio.error import S3Error
@@ -75,8 +75,9 @@ class ConversionServiceImpl(conversion_connect.ConversionService):
if resolution not in _RESOLUTION_PRESET_BY_PROTO: if resolution not in _RESOLUTION_PRESET_BY_PROTO:
raise ConnectError(Code.INVALID_ARGUMENT, "resolution is invalid") raise ConnectError(Code.INVALID_ARGUMENT, "resolution is invalid")
conversion_id = str(uuid.uuid4()) ksuid = Ksuid()
bucket_name = f"oc-{conversion_id}" conversion_id = str(ksuid)
bucket_name = f"oc-{bytes(ksuid).hex()}"
upload_key = "input/source.pptx" upload_key = "input/source.pptx"
expires_at = utc_now() + timedelta(seconds=self._config.s3_session_ttl_seconds) expires_at = utc_now() + timedelta(seconds=self._config.s3_session_ttl_seconds)
+20
View File
@@ -292,6 +292,7 @@ dependencies = [
{ name = "connectrpc" }, { name = "connectrpc" },
{ name = "minio" }, { name = "minio" },
{ name = "officeconvert" }, { name = "officeconvert" },
{ name = "svix-ksuid" },
{ name = "uvicorn" }, { name = "uvicorn" },
] ]
@@ -300,6 +301,7 @@ requires-dist = [
{ name = "connectrpc", specifier = ">=0.6.0" }, { name = "connectrpc", specifier = ">=0.6.0" },
{ name = "minio", specifier = ">=7.2.18" }, { name = "minio", specifier = ">=7.2.18" },
{ name = "officeconvert", editable = "packages/officeconvert" }, { name = "officeconvert", editable = "packages/officeconvert" },
{ name = "svix-ksuid", specifier = ">=0.7.0" },
{ name = "uvicorn", specifier = ">=0.35.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" }, { 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]] [[package]]
name = "python-pptx" name = "python-pptx"
version = "1.0.2" 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" }, { 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]] [[package]]
name = "typing-extensions" name = "typing-extensions"
version = "4.15.0" version = "4.15.0"