commit b9c78974d8902bc8993a3c15f8a9ebbf34a7c9a4 Author: madipo2611 Date: Tue Aug 12 20:21:54 2025 +0300 v0.0.1 diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -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 diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..8fe8e99 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/sqldialects.xml b/.idea/sqldialects.xml new file mode 100644 index 0000000..c746b56 --- /dev/null +++ b/.idea/sqldialects.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/tailly_messages.iml b/.idea/tailly_messages.iml new file mode 100644 index 0000000..5e764c4 --- /dev/null +++ b/.idea/tailly_messages.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..2118a69 --- /dev/null +++ b/go.mod @@ -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 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..f36b96f --- /dev/null +++ b/go.sum @@ -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= diff --git a/migrations/0001_initial_schema.down.sql b/migrations/0001_initial_schema.down.sql new file mode 100644 index 0000000..b35941f --- /dev/null +++ b/migrations/0001_initial_schema.down.sql @@ -0,0 +1,2 @@ +DROP TABLE IF EXISTS messages CASCADE; +DROP TABLE IF EXISTS chats CASCADE; \ No newline at end of file diff --git a/migrations/0001_initial_schema.up.sql b/migrations/0001_initial_schema.up.sql new file mode 100644 index 0000000..98ca052 --- /dev/null +++ b/migrations/0001_initial_schema.up.sql @@ -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); \ No newline at end of file diff --git a/proto/messages.pb.go b/proto/messages.pb.go new file mode 100644 index 0000000..4ba56cd --- /dev/null +++ b/proto/messages.pb.go @@ -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 +} diff --git a/proto/messages.proto b/proto/messages.proto new file mode 100644 index 0000000..28a69a1 --- /dev/null +++ b/proto/messages.proto @@ -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; +} \ No newline at end of file diff --git a/proto/messages_grpc.pb.go b/proto/messages_grpc.pb.go new file mode 100644 index 0000000..ef713d7 --- /dev/null +++ b/proto/messages_grpc.pb.go @@ -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", +} diff --git a/server.go b/server.go new file mode 100644 index 0000000..8df770d --- /dev/null +++ b/server.go @@ -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) + } +}