This commit is contained in:
madipo2611 2025-08-12 20:21:54 +03:00
commit b9c78974d8
13 changed files with 2009 additions and 0 deletions

8
.idea/.gitignore generated vendored Normal file
View File

@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

8
.idea/modules.xml generated Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/tailly_messages.iml" filepath="$PROJECT_DIR$/.idea/tailly_messages.iml" />
</modules>
</component>
</project>

7
.idea/sqldialects.xml generated Normal file
View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="SqlDialectMappings">
<file url="file://$PROJECT_DIR$/migrations/0001_initial_schema.down.sql" dialect="GenericSQL" />
<file url="file://$PROJECT_DIR$/migrations/0001_initial_schema.up.sql" dialect="GenericSQL" />
</component>
</project>

9
.idea/tailly_messages.iml generated Normal file
View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="Go" enabled="true" />
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

6
.idea/vcs.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

28
go.mod Normal file
View File

@ -0,0 +1,28 @@
module tailly_messages
go 1.25rc2
require (
github.com/jackc/pgx/v4 v4.18.3
github.com/segmentio/kafka-go v0.4.48
google.golang.org/grpc v1.74.2
google.golang.org/protobuf v1.36.7
)
require (
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
github.com/jackc/pgconn v1.14.3 // indirect
github.com/jackc/pgio v1.0.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgproto3/v2 v2.3.3 // indirect
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
github.com/jackc/pgtype v1.14.0 // indirect
github.com/jackc/puddle v1.3.0 // indirect
github.com/klauspost/compress v1.15.9 // indirect
github.com/pierrec/lz4/v4 v4.1.15 // indirect
golang.org/x/crypto v0.38.0 // indirect
golang.org/x/net v0.40.0 // indirect
golang.org/x/sys v0.33.0 // indirect
golang.org/x/text v0.25.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a // indirect
)

256
go.sum Normal file
View File

@ -0,0 +1,256 @@
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA=
github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE=
github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s=
github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=
github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY=
github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
github.com/jackc/pgconn v1.14.3 h1:bVoTr12EGANZz66nZPkMInAV/KHD2TxH9npjXXgiB3w=
github.com/jackc/pgconn v1.14.3/go.mod h1:RZbme4uasqzybK2RK5c65VsHxoyaml09lx3tXOcO/VM=
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c=
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc=
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=
github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgproto3/v2 v2.3.3 h1:1HLSx5H+tXR9pW3in3zaztoEwQYRC9SQaYUHjTSUOag=
github.com/jackc/pgproto3/v2 v2.3.3/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM=
github.com/jackc/pgtype v1.14.0 h1:y+xUdabmyMkJLyApYuPj38mW+aAIqCe5uuBB51rH3Vw=
github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=
github.com/jackc/pgx/v4 v4.18.3 h1:dE2/TrEsGX3RBprb3qryqSV9Y60iZN1C6i8IrmW9/BA=
github.com/jackc/pgx/v4 v4.18.3/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw=
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.3.0 h1:eHK/5clGOatcjX3oWGBO/MpxpbHzSwud5EWTSCI+MX0=
github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY=
github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8=
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0=
github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/segmentio/kafka-go v0.4.48 h1:9jyu9CWK4W5W+SroCe8EffbrRZVqAOkuaLd/ApID4Vs=
github.com/segmentio/kafka-go v0.4.48/go.mod h1:HjF6XbOKh0Pjlkr5GVZxt6CsjjwnmhVOfURM5KMd8qg=
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg=
go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E=
go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE=
go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs=
go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs=
go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY=
go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis=
go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4=
go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w=
go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a h1:v2PbRU4K3llS09c7zodFpNePeamkAwG3mPrAery9VeE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
google.golang.org/grpc v1.74.2 h1:WoosgB65DlWVC9FqI82dGsZhWFNBSLjQ84bjROOpMu4=
google.golang.org/grpc v1.74.2/go.mod h1:CtQ+BGjaAIXHs/5YS3i473GqwBBa1zGQNevxdeBEXrM=
google.golang.org/protobuf v1.36.7 h1:IgrO7UwFQGJdRNXH/sQux4R1Dj1WAKcLElzeeRaXV2A=
google.golang.org/protobuf v1.36.7/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=

View File

@ -0,0 +1,2 @@
DROP TABLE IF EXISTS messages CASCADE;
DROP TABLE IF EXISTS chats CASCADE;

View File

@ -0,0 +1,23 @@
CREATE TABLE chats (
id SERIAL PRIMARY KEY,
user1_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
user2_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
UNIQUE(user1_id, user2_id), -- Уникальный чат между двумя пользователями
CHECK (user1_id < user2_id) -- Предотвращение дублирования чатов (1-2 и 2-1)
);
-- Сообщения
CREATE TABLE messages (
id SERIAL PRIMARY KEY,
chat_id INTEGER NOT NULL REFERENCES chats(id) ON DELETE CASCADE,
sender_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
content TEXT NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'sent' CHECK (status IN ('sent', 'delivered', 'read')),
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
);
-- Индексы для производительности
CREATE INDEX idx_messages_chat_id ON messages(chat_id);
CREATE INDEX idx_messages_sender_id ON messages(sender_id);
CREATE INDEX idx_chats_user1_id ON chats(user1_id);
CREATE INDEX idx_chats_user2_id ON chats(user2_id);

877
proto/messages.pb.go Normal file
View File

@ -0,0 +1,877 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.6
// protoc v3.21.12
// source: messages.proto
package proto
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type CreateChatRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
User1Id int32 `protobuf:"varint,1,opt,name=user1_id,json=user1Id,proto3" json:"user1_id,omitempty"`
User2Id int32 `protobuf:"varint,2,opt,name=user2_id,json=user2Id,proto3" json:"user2_id,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *CreateChatRequest) Reset() {
*x = CreateChatRequest{}
mi := &file_messages_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *CreateChatRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CreateChatRequest) ProtoMessage() {}
func (x *CreateChatRequest) ProtoReflect() protoreflect.Message {
mi := &file_messages_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use CreateChatRequest.ProtoReflect.Descriptor instead.
func (*CreateChatRequest) Descriptor() ([]byte, []int) {
return file_messages_proto_rawDescGZIP(), []int{0}
}
func (x *CreateChatRequest) GetUser1Id() int32 {
if x != nil {
return x.User1Id
}
return 0
}
func (x *CreateChatRequest) GetUser2Id() int32 {
if x != nil {
return x.User2Id
}
return 0
}
type SendMessageRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
ChatId int32 `protobuf:"varint,1,opt,name=chat_id,json=chatId,proto3" json:"chat_id,omitempty"`
SenderId int32 `protobuf:"varint,2,opt,name=sender_id,json=senderId,proto3" json:"sender_id,omitempty"`
Content string `protobuf:"bytes,3,opt,name=content,proto3" json:"content,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *SendMessageRequest) Reset() {
*x = SendMessageRequest{}
mi := &file_messages_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *SendMessageRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*SendMessageRequest) ProtoMessage() {}
func (x *SendMessageRequest) ProtoReflect() protoreflect.Message {
mi := &file_messages_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use SendMessageRequest.ProtoReflect.Descriptor instead.
func (*SendMessageRequest) Descriptor() ([]byte, []int) {
return file_messages_proto_rawDescGZIP(), []int{1}
}
func (x *SendMessageRequest) GetChatId() int32 {
if x != nil {
return x.ChatId
}
return 0
}
func (x *SendMessageRequest) GetSenderId() int32 {
if x != nil {
return x.SenderId
}
return 0
}
func (x *SendMessageRequest) GetContent() string {
if x != nil {
return x.Content
}
return ""
}
type GetChatRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
User1Id int32 `protobuf:"varint,1,opt,name=user1_id,json=user1Id,proto3" json:"user1_id,omitempty"`
User2Id int32 `protobuf:"varint,2,opt,name=user2_id,json=user2Id,proto3" json:"user2_id,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *GetChatRequest) Reset() {
*x = GetChatRequest{}
mi := &file_messages_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *GetChatRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetChatRequest) ProtoMessage() {}
func (x *GetChatRequest) ProtoReflect() protoreflect.Message {
mi := &file_messages_proto_msgTypes[2]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetChatRequest.ProtoReflect.Descriptor instead.
func (*GetChatRequest) Descriptor() ([]byte, []int) {
return file_messages_proto_rawDescGZIP(), []int{2}
}
func (x *GetChatRequest) GetUser1Id() int32 {
if x != nil {
return x.User1Id
}
return 0
}
func (x *GetChatRequest) GetUser2Id() int32 {
if x != nil {
return x.User2Id
}
return 0
}
type GetChatMessagesRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
ChatId int32 `protobuf:"varint,1,opt,name=chat_id,json=chatId,proto3" json:"chat_id,omitempty"`
Limit int32 `protobuf:"varint,2,opt,name=limit,proto3" json:"limit,omitempty"`
Offset int32 `protobuf:"varint,3,opt,name=offset,proto3" json:"offset,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *GetChatMessagesRequest) Reset() {
*x = GetChatMessagesRequest{}
mi := &file_messages_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *GetChatMessagesRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetChatMessagesRequest) ProtoMessage() {}
func (x *GetChatMessagesRequest) ProtoReflect() protoreflect.Message {
mi := &file_messages_proto_msgTypes[3]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetChatMessagesRequest.ProtoReflect.Descriptor instead.
func (*GetChatMessagesRequest) Descriptor() ([]byte, []int) {
return file_messages_proto_rawDescGZIP(), []int{3}
}
func (x *GetChatMessagesRequest) GetChatId() int32 {
if x != nil {
return x.ChatId
}
return 0
}
func (x *GetChatMessagesRequest) GetLimit() int32 {
if x != nil {
return x.Limit
}
return 0
}
func (x *GetChatMessagesRequest) GetOffset() int32 {
if x != nil {
return x.Offset
}
return 0
}
type GetUserChatsRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
UserId int32 `protobuf:"varint,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *GetUserChatsRequest) Reset() {
*x = GetUserChatsRequest{}
mi := &file_messages_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *GetUserChatsRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetUserChatsRequest) ProtoMessage() {}
func (x *GetUserChatsRequest) ProtoReflect() protoreflect.Message {
mi := &file_messages_proto_msgTypes[4]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetUserChatsRequest.ProtoReflect.Descriptor instead.
func (*GetUserChatsRequest) Descriptor() ([]byte, []int) {
return file_messages_proto_rawDescGZIP(), []int{4}
}
func (x *GetUserChatsRequest) GetUserId() int32 {
if x != nil {
return x.UserId
}
return 0
}
type UpdateMessageStatusRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
MessageId int32 `protobuf:"varint,1,opt,name=message_id,json=messageId,proto3" json:"message_id,omitempty"`
Status string `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *UpdateMessageStatusRequest) Reset() {
*x = UpdateMessageStatusRequest{}
mi := &file_messages_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *UpdateMessageStatusRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*UpdateMessageStatusRequest) ProtoMessage() {}
func (x *UpdateMessageStatusRequest) ProtoReflect() protoreflect.Message {
mi := &file_messages_proto_msgTypes[5]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use UpdateMessageStatusRequest.ProtoReflect.Descriptor instead.
func (*UpdateMessageStatusRequest) Descriptor() ([]byte, []int) {
return file_messages_proto_rawDescGZIP(), []int{5}
}
func (x *UpdateMessageStatusRequest) GetMessageId() int32 {
if x != nil {
return x.MessageId
}
return 0
}
func (x *UpdateMessageStatusRequest) GetStatus() string {
if x != nil {
return x.Status
}
return ""
}
type StreamMessagesRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
UserId int32 `protobuf:"varint,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *StreamMessagesRequest) Reset() {
*x = StreamMessagesRequest{}
mi := &file_messages_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *StreamMessagesRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*StreamMessagesRequest) ProtoMessage() {}
func (x *StreamMessagesRequest) ProtoReflect() protoreflect.Message {
mi := &file_messages_proto_msgTypes[6]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use StreamMessagesRequest.ProtoReflect.Descriptor instead.
func (*StreamMessagesRequest) Descriptor() ([]byte, []int) {
return file_messages_proto_rawDescGZIP(), []int{6}
}
func (x *StreamMessagesRequest) GetUserId() int32 {
if x != nil {
return x.UserId
}
return 0
}
type Message struct {
state protoimpl.MessageState `protogen:"open.v1"`
Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
ChatId int32 `protobuf:"varint,2,opt,name=chat_id,json=chatId,proto3" json:"chat_id,omitempty"`
SenderId int32 `protobuf:"varint,3,opt,name=sender_id,json=senderId,proto3" json:"sender_id,omitempty"`
Content string `protobuf:"bytes,4,opt,name=content,proto3" json:"content,omitempty"`
Status string `protobuf:"bytes,5,opt,name=status,proto3" json:"status,omitempty"`
CreatedAt *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *Message) Reset() {
*x = Message{}
mi := &file_messages_proto_msgTypes[7]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *Message) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Message) ProtoMessage() {}
func (x *Message) ProtoReflect() protoreflect.Message {
mi := &file_messages_proto_msgTypes[7]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Message.ProtoReflect.Descriptor instead.
func (*Message) Descriptor() ([]byte, []int) {
return file_messages_proto_rawDescGZIP(), []int{7}
}
func (x *Message) GetId() int32 {
if x != nil {
return x.Id
}
return 0
}
func (x *Message) GetChatId() int32 {
if x != nil {
return x.ChatId
}
return 0
}
func (x *Message) GetSenderId() int32 {
if x != nil {
return x.SenderId
}
return 0
}
func (x *Message) GetContent() string {
if x != nil {
return x.Content
}
return ""
}
func (x *Message) GetStatus() string {
if x != nil {
return x.Status
}
return ""
}
func (x *Message) GetCreatedAt() *timestamppb.Timestamp {
if x != nil {
return x.CreatedAt
}
return nil
}
type Chat struct {
state protoimpl.MessageState `protogen:"open.v1"`
Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
User1Id int32 `protobuf:"varint,2,opt,name=user1_id,json=user1Id,proto3" json:"user1_id,omitempty"`
User2Id int32 `protobuf:"varint,3,opt,name=user2_id,json=user2Id,proto3" json:"user2_id,omitempty"`
CreatedAt *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"`
UpdatedAt *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"`
LastMessage *Message `protobuf:"bytes,6,opt,name=last_message,json=lastMessage,proto3" json:"last_message,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *Chat) Reset() {
*x = Chat{}
mi := &file_messages_proto_msgTypes[8]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *Chat) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Chat) ProtoMessage() {}
func (x *Chat) ProtoReflect() protoreflect.Message {
mi := &file_messages_proto_msgTypes[8]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Chat.ProtoReflect.Descriptor instead.
func (*Chat) Descriptor() ([]byte, []int) {
return file_messages_proto_rawDescGZIP(), []int{8}
}
func (x *Chat) GetId() int32 {
if x != nil {
return x.Id
}
return 0
}
func (x *Chat) GetUser1Id() int32 {
if x != nil {
return x.User1Id
}
return 0
}
func (x *Chat) GetUser2Id() int32 {
if x != nil {
return x.User2Id
}
return 0
}
func (x *Chat) GetCreatedAt() *timestamppb.Timestamp {
if x != nil {
return x.CreatedAt
}
return nil
}
func (x *Chat) GetUpdatedAt() *timestamppb.Timestamp {
if x != nil {
return x.UpdatedAt
}
return nil
}
func (x *Chat) GetLastMessage() *Message {
if x != nil {
return x.LastMessage
}
return nil
}
type MessageResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
Message *Message `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *MessageResponse) Reset() {
*x = MessageResponse{}
mi := &file_messages_proto_msgTypes[9]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *MessageResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*MessageResponse) ProtoMessage() {}
func (x *MessageResponse) ProtoReflect() protoreflect.Message {
mi := &file_messages_proto_msgTypes[9]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use MessageResponse.ProtoReflect.Descriptor instead.
func (*MessageResponse) Descriptor() ([]byte, []int) {
return file_messages_proto_rawDescGZIP(), []int{9}
}
func (x *MessageResponse) GetMessage() *Message {
if x != nil {
return x.Message
}
return nil
}
type MessagesResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
Messages []*Message `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *MessagesResponse) Reset() {
*x = MessagesResponse{}
mi := &file_messages_proto_msgTypes[10]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *MessagesResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*MessagesResponse) ProtoMessage() {}
func (x *MessagesResponse) ProtoReflect() protoreflect.Message {
mi := &file_messages_proto_msgTypes[10]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use MessagesResponse.ProtoReflect.Descriptor instead.
func (*MessagesResponse) Descriptor() ([]byte, []int) {
return file_messages_proto_rawDescGZIP(), []int{10}
}
func (x *MessagesResponse) GetMessages() []*Message {
if x != nil {
return x.Messages
}
return nil
}
type ChatResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
Chat *Chat `protobuf:"bytes,1,opt,name=chat,proto3" json:"chat,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ChatResponse) Reset() {
*x = ChatResponse{}
mi := &file_messages_proto_msgTypes[11]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ChatResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ChatResponse) ProtoMessage() {}
func (x *ChatResponse) ProtoReflect() protoreflect.Message {
mi := &file_messages_proto_msgTypes[11]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ChatResponse.ProtoReflect.Descriptor instead.
func (*ChatResponse) Descriptor() ([]byte, []int) {
return file_messages_proto_rawDescGZIP(), []int{11}
}
func (x *ChatResponse) GetChat() *Chat {
if x != nil {
return x.Chat
}
return nil
}
type UserChatsResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
Chats []*Chat `protobuf:"bytes,1,rep,name=chats,proto3" json:"chats,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *UserChatsResponse) Reset() {
*x = UserChatsResponse{}
mi := &file_messages_proto_msgTypes[12]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *UserChatsResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*UserChatsResponse) ProtoMessage() {}
func (x *UserChatsResponse) ProtoReflect() protoreflect.Message {
mi := &file_messages_proto_msgTypes[12]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use UserChatsResponse.ProtoReflect.Descriptor instead.
func (*UserChatsResponse) Descriptor() ([]byte, []int) {
return file_messages_proto_rawDescGZIP(), []int{12}
}
func (x *UserChatsResponse) GetChats() []*Chat {
if x != nil {
return x.Chats
}
return nil
}
var File_messages_proto protoreflect.FileDescriptor
const file_messages_proto_rawDesc = "" +
"\n" +
"\x0emessages.proto\x12\x05proto\x1a\x1fgoogle/protobuf/timestamp.proto\"I\n" +
"\x11CreateChatRequest\x12\x19\n" +
"\buser1_id\x18\x01 \x01(\x05R\auser1Id\x12\x19\n" +
"\buser2_id\x18\x02 \x01(\x05R\auser2Id\"d\n" +
"\x12SendMessageRequest\x12\x17\n" +
"\achat_id\x18\x01 \x01(\x05R\x06chatId\x12\x1b\n" +
"\tsender_id\x18\x02 \x01(\x05R\bsenderId\x12\x18\n" +
"\acontent\x18\x03 \x01(\tR\acontent\"F\n" +
"\x0eGetChatRequest\x12\x19\n" +
"\buser1_id\x18\x01 \x01(\x05R\auser1Id\x12\x19\n" +
"\buser2_id\x18\x02 \x01(\x05R\auser2Id\"_\n" +
"\x16GetChatMessagesRequest\x12\x17\n" +
"\achat_id\x18\x01 \x01(\x05R\x06chatId\x12\x14\n" +
"\x05limit\x18\x02 \x01(\x05R\x05limit\x12\x16\n" +
"\x06offset\x18\x03 \x01(\x05R\x06offset\".\n" +
"\x13GetUserChatsRequest\x12\x17\n" +
"\auser_id\x18\x01 \x01(\x05R\x06userId\"S\n" +
"\x1aUpdateMessageStatusRequest\x12\x1d\n" +
"\n" +
"message_id\x18\x01 \x01(\x05R\tmessageId\x12\x16\n" +
"\x06status\x18\x02 \x01(\tR\x06status\"0\n" +
"\x15StreamMessagesRequest\x12\x17\n" +
"\auser_id\x18\x01 \x01(\x05R\x06userId\"\xbc\x01\n" +
"\aMessage\x12\x0e\n" +
"\x02id\x18\x01 \x01(\x05R\x02id\x12\x17\n" +
"\achat_id\x18\x02 \x01(\x05R\x06chatId\x12\x1b\n" +
"\tsender_id\x18\x03 \x01(\x05R\bsenderId\x12\x18\n" +
"\acontent\x18\x04 \x01(\tR\acontent\x12\x16\n" +
"\x06status\x18\x05 \x01(\tR\x06status\x129\n" +
"\n" +
"created_at\x18\x06 \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\"\xf5\x01\n" +
"\x04Chat\x12\x0e\n" +
"\x02id\x18\x01 \x01(\x05R\x02id\x12\x19\n" +
"\buser1_id\x18\x02 \x01(\x05R\auser1Id\x12\x19\n" +
"\buser2_id\x18\x03 \x01(\x05R\auser2Id\x129\n" +
"\n" +
"created_at\x18\x04 \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\x129\n" +
"\n" +
"updated_at\x18\x05 \x01(\v2\x1a.google.protobuf.TimestampR\tupdatedAt\x121\n" +
"\flast_message\x18\x06 \x01(\v2\x0e.proto.MessageR\vlastMessage\";\n" +
"\x0fMessageResponse\x12(\n" +
"\amessage\x18\x01 \x01(\v2\x0e.proto.MessageR\amessage\">\n" +
"\x10MessagesResponse\x12*\n" +
"\bmessages\x18\x01 \x03(\v2\x0e.proto.MessageR\bmessages\"/\n" +
"\fChatResponse\x12\x1f\n" +
"\x04chat\x18\x01 \x01(\v2\v.proto.ChatR\x04chat\"6\n" +
"\x11UserChatsResponse\x12!\n" +
"\x05chats\x18\x01 \x03(\v2\v.proto.ChatR\x05chats2\xf3\x03\n" +
"\x0eMessageService\x12;\n" +
"\n" +
"CreateChat\x12\x18.proto.CreateChatRequest\x1a\x13.proto.ChatResponse\x12@\n" +
"\vSendMessage\x12\x19.proto.SendMessageRequest\x1a\x16.proto.MessageResponse\x125\n" +
"\aGetChat\x12\x15.proto.GetChatRequest\x1a\x13.proto.ChatResponse\x12I\n" +
"\x0fGetChatMessages\x12\x1d.proto.GetChatMessagesRequest\x1a\x17.proto.MessagesResponse\x12D\n" +
"\fGetUserChats\x12\x1a.proto.GetUserChatsRequest\x1a\x18.proto.UserChatsResponse\x12P\n" +
"\x13UpdateMessageStatus\x12!.proto.UpdateMessageStatusRequest\x1a\x16.proto.MessageResponse\x12H\n" +
"\x0eStreamMessages\x12\x1c.proto.StreamMessagesRequest\x1a\x16.proto.MessageResponse0\x01B\n" +
"Z\b./;protob\x06proto3"
var (
file_messages_proto_rawDescOnce sync.Once
file_messages_proto_rawDescData []byte
)
func file_messages_proto_rawDescGZIP() []byte {
file_messages_proto_rawDescOnce.Do(func() {
file_messages_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_messages_proto_rawDesc), len(file_messages_proto_rawDesc)))
})
return file_messages_proto_rawDescData
}
var file_messages_proto_msgTypes = make([]protoimpl.MessageInfo, 13)
var file_messages_proto_goTypes = []any{
(*CreateChatRequest)(nil), // 0: proto.CreateChatRequest
(*SendMessageRequest)(nil), // 1: proto.SendMessageRequest
(*GetChatRequest)(nil), // 2: proto.GetChatRequest
(*GetChatMessagesRequest)(nil), // 3: proto.GetChatMessagesRequest
(*GetUserChatsRequest)(nil), // 4: proto.GetUserChatsRequest
(*UpdateMessageStatusRequest)(nil), // 5: proto.UpdateMessageStatusRequest
(*StreamMessagesRequest)(nil), // 6: proto.StreamMessagesRequest
(*Message)(nil), // 7: proto.Message
(*Chat)(nil), // 8: proto.Chat
(*MessageResponse)(nil), // 9: proto.MessageResponse
(*MessagesResponse)(nil), // 10: proto.MessagesResponse
(*ChatResponse)(nil), // 11: proto.ChatResponse
(*UserChatsResponse)(nil), // 12: proto.UserChatsResponse
(*timestamppb.Timestamp)(nil), // 13: google.protobuf.Timestamp
}
var file_messages_proto_depIdxs = []int32{
13, // 0: proto.Message.created_at:type_name -> google.protobuf.Timestamp
13, // 1: proto.Chat.created_at:type_name -> google.protobuf.Timestamp
13, // 2: proto.Chat.updated_at:type_name -> google.protobuf.Timestamp
7, // 3: proto.Chat.last_message:type_name -> proto.Message
7, // 4: proto.MessageResponse.message:type_name -> proto.Message
7, // 5: proto.MessagesResponse.messages:type_name -> proto.Message
8, // 6: proto.ChatResponse.chat:type_name -> proto.Chat
8, // 7: proto.UserChatsResponse.chats:type_name -> proto.Chat
0, // 8: proto.MessageService.CreateChat:input_type -> proto.CreateChatRequest
1, // 9: proto.MessageService.SendMessage:input_type -> proto.SendMessageRequest
2, // 10: proto.MessageService.GetChat:input_type -> proto.GetChatRequest
3, // 11: proto.MessageService.GetChatMessages:input_type -> proto.GetChatMessagesRequest
4, // 12: proto.MessageService.GetUserChats:input_type -> proto.GetUserChatsRequest
5, // 13: proto.MessageService.UpdateMessageStatus:input_type -> proto.UpdateMessageStatusRequest
6, // 14: proto.MessageService.StreamMessages:input_type -> proto.StreamMessagesRequest
11, // 15: proto.MessageService.CreateChat:output_type -> proto.ChatResponse
9, // 16: proto.MessageService.SendMessage:output_type -> proto.MessageResponse
11, // 17: proto.MessageService.GetChat:output_type -> proto.ChatResponse
10, // 18: proto.MessageService.GetChatMessages:output_type -> proto.MessagesResponse
12, // 19: proto.MessageService.GetUserChats:output_type -> proto.UserChatsResponse
9, // 20: proto.MessageService.UpdateMessageStatus:output_type -> proto.MessageResponse
9, // 21: proto.MessageService.StreamMessages:output_type -> proto.MessageResponse
15, // [15:22] is the sub-list for method output_type
8, // [8:15] is the sub-list for method input_type
8, // [8:8] is the sub-list for extension type_name
8, // [8:8] is the sub-list for extension extendee
0, // [0:8] is the sub-list for field type_name
}
func init() { file_messages_proto_init() }
func file_messages_proto_init() {
if File_messages_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_messages_proto_rawDesc), len(file_messages_proto_rawDesc)),
NumEnums: 0,
NumMessages: 13,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_messages_proto_goTypes,
DependencyIndexes: file_messages_proto_depIdxs,
MessageInfos: file_messages_proto_msgTypes,
}.Build()
File_messages_proto = out.File
file_messages_proto_goTypes = nil
file_messages_proto_depIdxs = nil
}

86
proto/messages.proto Normal file
View File

@ -0,0 +1,86 @@
syntax = "proto3";
package proto;
option go_package = "./;proto";
import "google/protobuf/timestamp.proto";
service MessageService {
rpc CreateChat (CreateChatRequest) returns (ChatResponse);
rpc SendMessage (SendMessageRequest) returns (MessageResponse);
rpc GetChat (GetChatRequest) returns (ChatResponse);
rpc GetChatMessages (GetChatMessagesRequest) returns (MessagesResponse);
rpc GetUserChats (GetUserChatsRequest) returns (UserChatsResponse);
rpc UpdateMessageStatus (UpdateMessageStatusRequest) returns (MessageResponse);
rpc StreamMessages (StreamMessagesRequest) returns (stream MessageResponse);
}
message CreateChatRequest {
int32 user1_id = 1;
int32 user2_id = 2;
}
message SendMessageRequest {
int32 chat_id = 1;
int32 sender_id = 2;
string content = 3;
}
message GetChatRequest {
int32 user1_id = 1;
int32 user2_id = 2;
}
message GetChatMessagesRequest {
int32 chat_id = 1;
int32 limit = 2;
int32 offset = 3;
}
message GetUserChatsRequest {
int32 user_id = 1;
}
message UpdateMessageStatusRequest {
int32 message_id = 1;
string status = 2;
}
message StreamMessagesRequest {
int32 user_id = 1;
}
message Message {
int32 id = 1;
int32 chat_id = 2;
int32 sender_id = 3;
string content = 4;
string status = 5;
google.protobuf.Timestamp created_at = 6;
}
message Chat {
int32 id = 1;
int32 user1_id = 2;
int32 user2_id = 3;
google.protobuf.Timestamp created_at = 4;
google.protobuf.Timestamp updated_at = 5;
Message last_message = 6;
}
message MessageResponse {
Message message = 1;
}
message MessagesResponse {
repeated Message messages = 1;
}
message ChatResponse {
Chat chat = 1;
}
message UserChatsResponse {
repeated Chat chats = 1;
}

353
proto/messages_grpc.pb.go Normal file
View File

@ -0,0 +1,353 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.5.1
// - protoc v3.21.12
// source: messages.proto
package proto
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.64.0 or later.
const _ = grpc.SupportPackageIsVersion9
const (
MessageService_CreateChat_FullMethodName = "/proto.MessageService/CreateChat"
MessageService_SendMessage_FullMethodName = "/proto.MessageService/SendMessage"
MessageService_GetChat_FullMethodName = "/proto.MessageService/GetChat"
MessageService_GetChatMessages_FullMethodName = "/proto.MessageService/GetChatMessages"
MessageService_GetUserChats_FullMethodName = "/proto.MessageService/GetUserChats"
MessageService_UpdateMessageStatus_FullMethodName = "/proto.MessageService/UpdateMessageStatus"
MessageService_StreamMessages_FullMethodName = "/proto.MessageService/StreamMessages"
)
// MessageServiceClient is the client API for MessageService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type MessageServiceClient interface {
CreateChat(ctx context.Context, in *CreateChatRequest, opts ...grpc.CallOption) (*ChatResponse, error)
SendMessage(ctx context.Context, in *SendMessageRequest, opts ...grpc.CallOption) (*MessageResponse, error)
GetChat(ctx context.Context, in *GetChatRequest, opts ...grpc.CallOption) (*ChatResponse, error)
GetChatMessages(ctx context.Context, in *GetChatMessagesRequest, opts ...grpc.CallOption) (*MessagesResponse, error)
GetUserChats(ctx context.Context, in *GetUserChatsRequest, opts ...grpc.CallOption) (*UserChatsResponse, error)
UpdateMessageStatus(ctx context.Context, in *UpdateMessageStatusRequest, opts ...grpc.CallOption) (*MessageResponse, error)
StreamMessages(ctx context.Context, in *StreamMessagesRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[MessageResponse], error)
}
type messageServiceClient struct {
cc grpc.ClientConnInterface
}
func NewMessageServiceClient(cc grpc.ClientConnInterface) MessageServiceClient {
return &messageServiceClient{cc}
}
func (c *messageServiceClient) CreateChat(ctx context.Context, in *CreateChatRequest, opts ...grpc.CallOption) (*ChatResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ChatResponse)
err := c.cc.Invoke(ctx, MessageService_CreateChat_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *messageServiceClient) SendMessage(ctx context.Context, in *SendMessageRequest, opts ...grpc.CallOption) (*MessageResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(MessageResponse)
err := c.cc.Invoke(ctx, MessageService_SendMessage_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *messageServiceClient) GetChat(ctx context.Context, in *GetChatRequest, opts ...grpc.CallOption) (*ChatResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ChatResponse)
err := c.cc.Invoke(ctx, MessageService_GetChat_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *messageServiceClient) GetChatMessages(ctx context.Context, in *GetChatMessagesRequest, opts ...grpc.CallOption) (*MessagesResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(MessagesResponse)
err := c.cc.Invoke(ctx, MessageService_GetChatMessages_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *messageServiceClient) GetUserChats(ctx context.Context, in *GetUserChatsRequest, opts ...grpc.CallOption) (*UserChatsResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(UserChatsResponse)
err := c.cc.Invoke(ctx, MessageService_GetUserChats_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *messageServiceClient) UpdateMessageStatus(ctx context.Context, in *UpdateMessageStatusRequest, opts ...grpc.CallOption) (*MessageResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(MessageResponse)
err := c.cc.Invoke(ctx, MessageService_UpdateMessageStatus_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *messageServiceClient) StreamMessages(ctx context.Context, in *StreamMessagesRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[MessageResponse], error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
stream, err := c.cc.NewStream(ctx, &MessageService_ServiceDesc.Streams[0], MessageService_StreamMessages_FullMethodName, cOpts...)
if err != nil {
return nil, err
}
x := &grpc.GenericClientStream[StreamMessagesRequest, MessageResponse]{ClientStream: stream}
if err := x.ClientStream.SendMsg(in); err != nil {
return nil, err
}
if err := x.ClientStream.CloseSend(); err != nil {
return nil, err
}
return x, nil
}
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
type MessageService_StreamMessagesClient = grpc.ServerStreamingClient[MessageResponse]
// MessageServiceServer is the server API for MessageService service.
// All implementations must embed UnimplementedMessageServiceServer
// for forward compatibility.
type MessageServiceServer interface {
CreateChat(context.Context, *CreateChatRequest) (*ChatResponse, error)
SendMessage(context.Context, *SendMessageRequest) (*MessageResponse, error)
GetChat(context.Context, *GetChatRequest) (*ChatResponse, error)
GetChatMessages(context.Context, *GetChatMessagesRequest) (*MessagesResponse, error)
GetUserChats(context.Context, *GetUserChatsRequest) (*UserChatsResponse, error)
UpdateMessageStatus(context.Context, *UpdateMessageStatusRequest) (*MessageResponse, error)
StreamMessages(*StreamMessagesRequest, grpc.ServerStreamingServer[MessageResponse]) error
mustEmbedUnimplementedMessageServiceServer()
}
// UnimplementedMessageServiceServer must be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedMessageServiceServer struct{}
func (UnimplementedMessageServiceServer) CreateChat(context.Context, *CreateChatRequest) (*ChatResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateChat not implemented")
}
func (UnimplementedMessageServiceServer) SendMessage(context.Context, *SendMessageRequest) (*MessageResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method SendMessage not implemented")
}
func (UnimplementedMessageServiceServer) GetChat(context.Context, *GetChatRequest) (*ChatResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetChat not implemented")
}
func (UnimplementedMessageServiceServer) GetChatMessages(context.Context, *GetChatMessagesRequest) (*MessagesResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetChatMessages not implemented")
}
func (UnimplementedMessageServiceServer) GetUserChats(context.Context, *GetUserChatsRequest) (*UserChatsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetUserChats not implemented")
}
func (UnimplementedMessageServiceServer) UpdateMessageStatus(context.Context, *UpdateMessageStatusRequest) (*MessageResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method UpdateMessageStatus not implemented")
}
func (UnimplementedMessageServiceServer) StreamMessages(*StreamMessagesRequest, grpc.ServerStreamingServer[MessageResponse]) error {
return status.Errorf(codes.Unimplemented, "method StreamMessages not implemented")
}
func (UnimplementedMessageServiceServer) mustEmbedUnimplementedMessageServiceServer() {}
func (UnimplementedMessageServiceServer) testEmbeddedByValue() {}
// UnsafeMessageServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to MessageServiceServer will
// result in compilation errors.
type UnsafeMessageServiceServer interface {
mustEmbedUnimplementedMessageServiceServer()
}
func RegisterMessageServiceServer(s grpc.ServiceRegistrar, srv MessageServiceServer) {
// If the following call pancis, it indicates UnimplementedMessageServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&MessageService_ServiceDesc, srv)
}
func _MessageService_CreateChat_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CreateChatRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(MessageServiceServer).CreateChat(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: MessageService_CreateChat_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(MessageServiceServer).CreateChat(ctx, req.(*CreateChatRequest))
}
return interceptor(ctx, in, info, handler)
}
func _MessageService_SendMessage_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SendMessageRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(MessageServiceServer).SendMessage(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: MessageService_SendMessage_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(MessageServiceServer).SendMessage(ctx, req.(*SendMessageRequest))
}
return interceptor(ctx, in, info, handler)
}
func _MessageService_GetChat_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetChatRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(MessageServiceServer).GetChat(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: MessageService_GetChat_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(MessageServiceServer).GetChat(ctx, req.(*GetChatRequest))
}
return interceptor(ctx, in, info, handler)
}
func _MessageService_GetChatMessages_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetChatMessagesRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(MessageServiceServer).GetChatMessages(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: MessageService_GetChatMessages_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(MessageServiceServer).GetChatMessages(ctx, req.(*GetChatMessagesRequest))
}
return interceptor(ctx, in, info, handler)
}
func _MessageService_GetUserChats_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetUserChatsRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(MessageServiceServer).GetUserChats(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: MessageService_GetUserChats_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(MessageServiceServer).GetUserChats(ctx, req.(*GetUserChatsRequest))
}
return interceptor(ctx, in, info, handler)
}
func _MessageService_UpdateMessageStatus_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(UpdateMessageStatusRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(MessageServiceServer).UpdateMessageStatus(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: MessageService_UpdateMessageStatus_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(MessageServiceServer).UpdateMessageStatus(ctx, req.(*UpdateMessageStatusRequest))
}
return interceptor(ctx, in, info, handler)
}
func _MessageService_StreamMessages_Handler(srv interface{}, stream grpc.ServerStream) error {
m := new(StreamMessagesRequest)
if err := stream.RecvMsg(m); err != nil {
return err
}
return srv.(MessageServiceServer).StreamMessages(m, &grpc.GenericServerStream[StreamMessagesRequest, MessageResponse]{ServerStream: stream})
}
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
type MessageService_StreamMessagesServer = grpc.ServerStreamingServer[MessageResponse]
// MessageService_ServiceDesc is the grpc.ServiceDesc for MessageService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var MessageService_ServiceDesc = grpc.ServiceDesc{
ServiceName: "proto.MessageService",
HandlerType: (*MessageServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "CreateChat",
Handler: _MessageService_CreateChat_Handler,
},
{
MethodName: "SendMessage",
Handler: _MessageService_SendMessage_Handler,
},
{
MethodName: "GetChat",
Handler: _MessageService_GetChat_Handler,
},
{
MethodName: "GetChatMessages",
Handler: _MessageService_GetChatMessages_Handler,
},
{
MethodName: "GetUserChats",
Handler: _MessageService_GetUserChats_Handler,
},
{
MethodName: "UpdateMessageStatus",
Handler: _MessageService_UpdateMessageStatus_Handler,
},
},
Streams: []grpc.StreamDesc{
{
StreamName: "StreamMessages",
Handler: _MessageService_StreamMessages_Handler,
ServerStreams: true,
},
},
Metadata: "messages.proto",
}

346
server.go Normal file
View File

@ -0,0 +1,346 @@
package main
import (
"context"
"database/sql"
"fmt"
"github.com/jackc/pgx/v4/pgxpool"
"github.com/segmentio/kafka-go"
"google.golang.org/grpc"
protobuf "google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/timestamppb"
"log"
"net"
"tailly_messages/proto"
"time"
)
type server struct {
proto.UnimplementedMessageServiceServer
db *pgxpool.Pool
producer *kafka.Writer
}
func NewServer(db *pgxpool.Pool, producer *kafka.Writer) *server {
return &server{db: db, producer: producer}
}
func (s *server) CreateChat(ctx context.Context, req *proto.CreateChatRequest) (*proto.ChatResponse, error) {
log.Printf("CreateChat request received: user1_id=%d, user2_id=%d", req.GetUser1Id(), req.GetUser2Id())
user1, user2 := req.GetUser1Id(), req.GetUser2Id()
if user1 > user2 {
user1, user2 = user2, user1
}
// Проверка существования пользователей
var user1Exists, user2Exists bool
err := s.db.QueryRow(ctx, "SELECT EXISTS(SELECT 1 FROM users WHERE id = $1)", user1).Scan(&user1Exists)
if err != nil {
log.Printf("Error checking user1 existence: %v", err)
return nil, fmt.Errorf("failed to check user existence")
}
err = s.db.QueryRow(ctx, "SELECT EXISTS(SELECT 1 FROM users WHERE id = $1)", user2).Scan(&user2Exists)
if err != nil {
log.Printf("Error checking user2 existence: %v", err)
return nil, fmt.Errorf("failed to check user existence")
}
if !user1Exists || !user2Exists {
errMsg := fmt.Sprintf("One or both users don't exist: user1=%d (%t), user2=%d (%t)",
user1, user1Exists, user2, user2Exists)
log.Println(errMsg)
return nil, fmt.Errorf(errMsg)
}
var chat proto.Chat
var createdAt, updatedAt time.Time
// Проверяем, не существует ли уже чат
var chatExists bool
err = s.db.QueryRow(ctx, "SELECT EXISTS(SELECT 1 FROM chats WHERE user1_id = $1 AND user2_id = $2)",
user1, user2).Scan(&chatExists)
if err != nil {
log.Printf("Error checking chat existence: %v", err)
return nil, fmt.Errorf("failed to check chat existence")
}
if chatExists {
log.Printf("Chat already exists between users %d and %d", user1, user2)
return s.GetChat(ctx, &proto.GetChatRequest{
User1Id: user1,
User2Id: user2,
})
}
// Создаем новый чат
err = s.db.QueryRow(ctx, `
INSERT INTO chats (user1_id, user2_id)
VALUES ($1, $2)
RETURNING id, user1_id, user2_id, created_at, updated_at
`, user1, user2).Scan(
&chat.Id, &chat.User1Id, &chat.User2Id, &createdAt, &updatedAt,
)
if err != nil {
log.Printf("Failed to create chat: %v", err)
return nil, fmt.Errorf("failed to create chat: %v", err)
}
log.Printf("Successfully created new chat: id=%d, user1_id=%d, user2_id=%d",
chat.Id, chat.User1Id, chat.User2Id)
chat.CreatedAt = timestamppb.New(createdAt)
chat.UpdatedAt = timestamppb.New(updatedAt)
return &proto.ChatResponse{Chat: &chat}, nil
}
func (s *server) SendMessage(ctx context.Context, req *proto.SendMessageRequest) (*proto.MessageResponse, error) {
var message proto.Message
var createdAt time.Time
err := s.db.QueryRow(ctx, `
INSERT INTO messages (chat_id, sender_id, content)
VALUES ($1, $2, $3)
RETURNING id, chat_id, sender_id, content, status, created_at
`, req.ChatId, req.SenderId, req.Content).Scan(
&message.Id, &message.ChatId, &message.SenderId, &message.Content, &message.Status, &createdAt,
)
if err != nil {
return nil, err
}
message.CreatedAt = timestamppb.New(createdAt)
// Update chat's updated_at
_, err = s.db.Exec(ctx, `
UPDATE chats SET updated_at = NOW() WHERE id = $1
`, req.ChatId)
if err != nil {
return nil, err
}
// Publish to Kafka for real-time delivery
err = s.producer.WriteMessages(ctx, kafka.Message{
Key: []byte(string(req.ChatId)),
Value: []byte(message.String()),
})
if err != nil {
log.Printf("Failed to publish message to Kafka: %v", err)
}
return &proto.MessageResponse{Message: &message}, nil
}
func (s *server) GetChat(ctx context.Context, req *proto.GetChatRequest) (*proto.ChatResponse, error) {
var chat proto.Chat
var createdAt, updatedAt time.Time
var lastMessageID sql.NullInt32
var lastMessageContent sql.NullString
var lastMessageStatus sql.NullString
var lastMessageCreatedAt sql.NullTime
user1, user2 := req.User1Id, req.User2Id
if user1 > user2 {
user1, user2 = user2, user1
}
err := s.db.QueryRow(ctx, `
SELECT c.id, c.user1_id, c.user2_id, c.created_at, c.updated_at,
m.id, m.content, m.status, m.created_at
FROM chats c
LEFT JOIN messages m ON m.id = (
SELECT id FROM messages WHERE chat_id = c.id
ORDER BY created_at DESC LIMIT 1
)
WHERE c.user1_id = $1 AND c.user2_id = $2
`, user1, user2).Scan(
&chat.Id, &chat.User1Id, &chat.User2Id, &createdAt, &updatedAt,
&lastMessageID, &lastMessageContent, &lastMessageStatus, &lastMessageCreatedAt,
)
if err != nil {
return nil, err
}
chat.CreatedAt = timestamppb.New(createdAt)
chat.UpdatedAt = timestamppb.New(updatedAt)
if lastMessageID.Valid {
chat.LastMessage = &proto.Message{
Id: lastMessageID.Int32,
ChatId: chat.Id,
Content: lastMessageContent.String,
Status: lastMessageStatus.String,
CreatedAt: timestamppb.New(lastMessageCreatedAt.Time),
}
}
return &proto.ChatResponse{Chat: &chat}, nil
}
func (s *server) GetChatMessages(ctx context.Context, req *proto.GetChatMessagesRequest) (*proto.MessagesResponse, error) {
rows, err := s.db.Query(ctx, `
SELECT id, chat_id, sender_id, content, status, created_at
FROM messages
WHERE chat_id = $1
ORDER BY created_at DESC
LIMIT $2 OFFSET $3
`, req.ChatId, req.Limit, req.Offset)
if err != nil {
return nil, err
}
defer rows.Close()
var messages []*proto.Message
for rows.Next() {
var msg proto.Message
var createdAt time.Time
err := rows.Scan(
&msg.Id, &msg.ChatId, &msg.SenderId, &msg.Content, &msg.Status, &createdAt,
)
if err != nil {
return nil, err
}
msg.CreatedAt = timestamppb.New(createdAt)
messages = append(messages, &msg)
}
return &proto.MessagesResponse{Messages: messages}, nil
}
func (s *server) GetUserChats(ctx context.Context, req *proto.GetUserChatsRequest) (*proto.UserChatsResponse, error) {
rows, err := s.db.Query(ctx, `
SELECT c.id, c.user1_id, c.user2_id, c.created_at, c.updated_at,
m.id, m.content, m.status, m.created_at
FROM chats c
LEFT JOIN messages m ON m.id = (
SELECT id FROM messages WHERE chat_id = c.id
ORDER BY created_at DESC LIMIT 1
)
WHERE c.user1_id = $1 OR c.user2_id = $1
ORDER BY c.updated_at DESC
`, req.UserId)
if err != nil {
return nil, err
}
defer rows.Close()
var chats []*proto.Chat
for rows.Next() {
var chat proto.Chat
var createdAt, updatedAt time.Time
var lastMessageID sql.NullInt32
var lastMessageContent sql.NullString
var lastMessageStatus sql.NullString
var lastMessageCreatedAt sql.NullTime
err := rows.Scan(
&chat.Id, &chat.User1Id, &chat.User2Id, &createdAt, &updatedAt,
&lastMessageID, &lastMessageContent, &lastMessageStatus, &lastMessageCreatedAt,
)
if err != nil {
return nil, err
}
chat.CreatedAt = timestamppb.New(createdAt)
chat.UpdatedAt = timestamppb.New(updatedAt)
if lastMessageID.Valid {
chat.LastMessage = &proto.Message{
Id: lastMessageID.Int32,
ChatId: chat.Id,
Content: lastMessageContent.String,
Status: lastMessageStatus.String,
CreatedAt: timestamppb.New(lastMessageCreatedAt.Time),
}
}
chats = append(chats, &chat)
}
return &proto.UserChatsResponse{Chats: chats}, nil
}
func (s *server) UpdateMessageStatus(ctx context.Context, req *proto.UpdateMessageStatusRequest) (*proto.MessageResponse, error) {
var message proto.Message
var createdAt time.Time
err := s.db.QueryRow(ctx, `
UPDATE messages
SET status = $1
WHERE id = $2
RETURNING id, chat_id, sender_id, content, status, created_at
`, req.Status, req.MessageId).Scan(
&message.Id, &message.ChatId, &message.SenderId, &message.Content, &message.Status, &createdAt,
)
if err != nil {
return nil, err
}
message.CreatedAt = timestamppb.New(createdAt)
return &proto.MessageResponse{Message: &message}, nil
}
func (s *server) StreamMessages(req *proto.StreamMessagesRequest, stream proto.MessageService_StreamMessagesServer) error {
// Create Kafka reader for this user's messages
reader := kafka.NewReader(kafka.ReaderConfig{
Brokers: []string{"localhost:9092"},
Topic: "user_messages",
Partition: 0,
MinBytes: 10e3, // 10KB
MaxBytes: 10e6, // 10MB
})
defer reader.Close()
for {
m, err := reader.ReadMessage(stream.Context())
if err != nil {
return err
}
// Check if message is for this user
if string(m.Key) == string(req.UserId) {
var message proto.Message
if err := protobuf.Unmarshal(m.Value, &message); err != nil {
log.Printf("Failed to unmarshal message: %v", err)
continue
}
if err := stream.Send(&proto.MessageResponse{Message: &message}); err != nil {
return err
}
}
}
}
func main() {
// Initialize database connection
pool, err := pgxpool.Connect(context.Background(), "postgres://tailly_v2:BBP%263XP956%26D8y6cYJ@79.174.89.104:15452/tailly_v2")
if err != nil {
log.Fatalf("Unable to connect to database: %v", err)
}
defer pool.Close()
// Initialize Kafka producer
producer := &kafka.Writer{
Addr: kafka.TCP("89.104.69.222:9092"),
Topic: "user_messages",
Balancer: &kafka.Hash{},
}
defer producer.Close()
// Create gRPC server
grpcServer := grpc.NewServer()
proto.RegisterMessageServiceServer(grpcServer, NewServer(pool, producer))
// Start server
lis, err := net.Listen("tcp", ":50051")
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
log.Println("Server started on port 50051")
if err := grpcServer.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}