mirror of
https://github.com/Alexander-D-Karpov/concord.git
synced 2026-03-16 22:04:15 +03:00
Compare commits
No commits in common. "master" and "v20251030-30e3fb9" have entirely different histories.
master
...
v20251030-
|
|
@ -78,12 +78,3 @@ HEALTH_PATH=/health
|
||||||
|
|
||||||
# Voice health port
|
# Voice health port
|
||||||
VOICE_HEALTH_PORT=8082
|
VOICE_HEALTH_PORT=8082
|
||||||
|
|
||||||
# Storage Configuration
|
|
||||||
STORAGE_PATH=./uploads
|
|
||||||
STORAGE_URL=http://localhost:8080/files
|
|
||||||
|
|
||||||
# Rate Limiting (updated)
|
|
||||||
RATE_LIMIT_ENABLED=true
|
|
||||||
RATE_LIMIT_REQUESTS_PER_MINUTE=120
|
|
||||||
RATE_LIMIT_BURST=20
|
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -1,5 +1,3 @@
|
||||||
uploads
|
|
||||||
|
|
||||||
# Binaries
|
# Binaries
|
||||||
/bin/
|
/bin/
|
||||||
*.exe
|
*.exe
|
||||||
|
|
|
||||||
78
Makefile
78
Makefile
|
|
@ -1,20 +1,7 @@
|
||||||
.PHONY: proto build clean clean-proto-deps test test-unit test-integration test-coverage test-cleanup \
|
.PHONY: proto build clean test test-unit test-integration run-api run-voice migrate docker-build lint deps all test-cleanup
|
||||||
run-api run-voice lint deps install-tools all \
|
|
||||||
up down restart rebuild ps logs logs-api logs-voice logs-db logs-redis \
|
|
||||||
update pull images
|
|
||||||
|
|
||||||
ifneq (,$(wildcard ./.env))
|
|
||||||
include .env
|
|
||||||
export
|
|
||||||
endif
|
|
||||||
|
|
||||||
COMPOSE_FILE := deploy/docker-compose.yml
|
|
||||||
ENV_FILE := .env
|
|
||||||
PROJECT := concord
|
|
||||||
DC := docker compose -p $(PROJECT) --env-file $(ENV_FILE) -f $(COMPOSE_FILE)
|
|
||||||
|
|
||||||
proto:
|
proto:
|
||||||
@sh scripts/gen_proto.sh
|
@bash scripts/gen_proto.sh
|
||||||
|
|
||||||
build:
|
build:
|
||||||
@echo "Building concord-api..."
|
@echo "Building concord-api..."
|
||||||
|
|
@ -25,9 +12,6 @@ build:
|
||||||
clean:
|
clean:
|
||||||
@rm -rf bin/ api/gen/
|
@rm -rf bin/ api/gen/
|
||||||
|
|
||||||
clean-proto-deps:
|
|
||||||
@rm -rf api/proto-deps/
|
|
||||||
|
|
||||||
test-cleanup:
|
test-cleanup:
|
||||||
@echo "Resetting test database..."
|
@echo "Resetting test database..."
|
||||||
@PGPASSWORD=postgres psql -h localhost -U postgres -d postgres -c "DROP DATABASE IF EXISTS concord_test;" 2>/dev/null || true
|
@PGPASSWORD=postgres psql -h localhost -U postgres -d postgres -c "DROP DATABASE IF EXISTS concord_test;" 2>/dev/null || true
|
||||||
|
|
@ -52,6 +36,10 @@ run-api:
|
||||||
run-voice:
|
run-voice:
|
||||||
@go run ./cmd/concord-voice
|
@go run ./cmd/concord-voice
|
||||||
|
|
||||||
|
docker-build:
|
||||||
|
@docker build -f deploy/Dockerfile.api -t concord-api:latest .
|
||||||
|
@docker build -f deploy/Dockerfile.voice -t concord-voice:latest .
|
||||||
|
|
||||||
lint:
|
lint:
|
||||||
@golangci-lint run ./...
|
@golangci-lint run ./...
|
||||||
|
|
||||||
|
|
@ -64,57 +52,5 @@ install-tools:
|
||||||
@go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
|
@go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
|
||||||
@go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway@latest
|
@go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway@latest
|
||||||
@go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2@latest
|
@go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2@latest
|
||||||
@go install github.com/bufbuild/buf/cmd/buf@v1.28.1
|
|
||||||
|
|
||||||
all: deps proto build
|
all: proto build
|
||||||
|
|
||||||
|
|
||||||
up:
|
|
||||||
@echo "Starting docker stack..."
|
|
||||||
@$(DC) up -d
|
|
||||||
@$(DC) ps
|
|
||||||
|
|
||||||
down:
|
|
||||||
@echo "Stopping docker stack..."
|
|
||||||
@$(DC) down
|
|
||||||
|
|
||||||
restart:
|
|
||||||
@echo "Restarting docker stack..."
|
|
||||||
@$(DC) restart
|
|
||||||
@$(DC) ps
|
|
||||||
|
|
||||||
rebuild:
|
|
||||||
@echo "Rebuilding and restarting docker stack..."
|
|
||||||
@$(DC) up -d --build --force-recreate
|
|
||||||
@$(DC) ps
|
|
||||||
|
|
||||||
ps:
|
|
||||||
@$(DC) ps
|
|
||||||
|
|
||||||
logs:
|
|
||||||
@$(DC) logs -f --tail=200
|
|
||||||
|
|
||||||
logs-api:
|
|
||||||
@$(DC) logs -f --tail=200 api
|
|
||||||
|
|
||||||
logs-voice:
|
|
||||||
@$(DC) logs -f --tail=200 voice
|
|
||||||
|
|
||||||
logs-db:
|
|
||||||
@$(DC) logs -f --tail=200 postgres
|
|
||||||
|
|
||||||
logs-redis:
|
|
||||||
@$(DC) logs -f --tail=200 redis
|
|
||||||
|
|
||||||
pull:
|
|
||||||
@echo "Pulling images..."
|
|
||||||
@$(DC) pull
|
|
||||||
|
|
||||||
images:
|
|
||||||
@$(DC) build
|
|
||||||
|
|
||||||
update:
|
|
||||||
@echo "Updating from git and redeploying..."
|
|
||||||
@git pull
|
|
||||||
@$(DC) up -d --build --force-recreate
|
|
||||||
@$(DC) ps
|
|
||||||
|
|
@ -4,8 +4,6 @@ package concord.admin.v1;
|
||||||
|
|
||||||
option go_package = "github.com/Alexander-D-Karpov/concord/api/gen/go/admin/v1;adminv1";
|
option go_package = "github.com/Alexander-D-Karpov/concord/api/gen/go/admin/v1;adminv1";
|
||||||
|
|
||||||
import "google/api/annotations.proto";
|
|
||||||
|
|
||||||
message KickRequest {
|
message KickRequest {
|
||||||
string room_id = 1;
|
string room_id = 1;
|
||||||
string user_id = 2;
|
string user_id = 2;
|
||||||
|
|
@ -26,22 +24,7 @@ message MuteRequest {
|
||||||
message EmptyResponse {}
|
message EmptyResponse {}
|
||||||
|
|
||||||
service AdminService {
|
service AdminService {
|
||||||
rpc Kick(KickRequest) returns (EmptyResponse) {
|
rpc Kick(KickRequest) returns (EmptyResponse);
|
||||||
option (google.api.http) = {
|
rpc Ban(BanRequest) returns (EmptyResponse);
|
||||||
post: "/v1/rooms/{room_id}/kick"
|
rpc Mute(MuteRequest) returns (EmptyResponse);
|
||||||
body: "*"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
rpc Ban(BanRequest) returns (EmptyResponse) {
|
|
||||||
option (google.api.http) = {
|
|
||||||
post: "/v1/rooms/{room_id}/ban"
|
|
||||||
body: "*"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
rpc Mute(MuteRequest) returns (EmptyResponse) {
|
|
||||||
option (google.api.http) = {
|
|
||||||
post: "/v1/rooms/{room_id}/mute"
|
|
||||||
body: "*"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -4,8 +4,6 @@ package concord.auth.v1;
|
||||||
|
|
||||||
option go_package = "github.com/Alexander-D-Karpov/concord/api/gen/go/auth/v1;authv1";
|
option go_package = "github.com/Alexander-D-Karpov/concord/api/gen/go/auth/v1;authv1";
|
||||||
|
|
||||||
import "google/api/annotations.proto";
|
|
||||||
|
|
||||||
message LoginPasswordRequest {
|
message LoginPasswordRequest {
|
||||||
string handle = 1;
|
string handle = 1;
|
||||||
string password = 2;
|
string password = 2;
|
||||||
|
|
@ -51,40 +49,10 @@ message RegisterRequest {
|
||||||
message EmptyResponse {}
|
message EmptyResponse {}
|
||||||
|
|
||||||
service AuthService {
|
service AuthService {
|
||||||
rpc Register(RegisterRequest) returns (Token) {
|
rpc Register(RegisterRequest) returns (Token);
|
||||||
option (google.api.http) = {
|
rpc LoginPassword(LoginPasswordRequest) returns (Token);
|
||||||
post: "/v1/auth/register"
|
rpc LoginOAuth(LoginOAuthRequest) returns (Token);
|
||||||
body: "*"
|
rpc OAuthBegin(OAuthBeginRequest) returns (OAuthBeginResponse);
|
||||||
};
|
rpc Refresh(RefreshRequest) returns (Token);
|
||||||
}
|
rpc Logout(LogoutRequest) returns (EmptyResponse);
|
||||||
rpc LoginPassword(LoginPasswordRequest) returns (Token) {
|
|
||||||
option (google.api.http) = {
|
|
||||||
post: "/v1/auth/login"
|
|
||||||
body: "*"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
rpc LoginOAuth(LoginOAuthRequest) returns (Token) {
|
|
||||||
option (google.api.http) = {
|
|
||||||
post: "/v1/auth/oauth"
|
|
||||||
body: "*"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
rpc OAuthBegin(OAuthBeginRequest) returns (OAuthBeginResponse) {
|
|
||||||
option (google.api.http) = {
|
|
||||||
post: "/v1/auth/oauth/begin"
|
|
||||||
body: "*"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
rpc Refresh(RefreshRequest) returns (Token) {
|
|
||||||
option (google.api.http) = {
|
|
||||||
post: "/v1/auth/refresh"
|
|
||||||
body: "*"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
rpc Logout(LogoutRequest) returns (EmptyResponse) {
|
|
||||||
option (google.api.http) = {
|
|
||||||
post: "/v1/auth/logout"
|
|
||||||
body: "*"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -12,9 +12,3 @@ plugins:
|
||||||
out: gen/go
|
out: gen/go
|
||||||
opt:
|
opt:
|
||||||
- paths=source_relative
|
- paths=source_relative
|
||||||
- plugin: buf.build/grpc-ecosystem/openapiv2
|
|
||||||
out: gen/openapiv2
|
|
||||||
opt:
|
|
||||||
- allow_merge=true
|
|
||||||
- merge_file_name=concord
|
|
||||||
- openapi_naming_strategy=fqn
|
|
||||||
|
|
@ -2,7 +2,6 @@ version: v1
|
||||||
name: buf.build/alexander-d-karpov/concord
|
name: buf.build/alexander-d-karpov/concord
|
||||||
deps:
|
deps:
|
||||||
- buf.build/googleapis/googleapis
|
- buf.build/googleapis/googleapis
|
||||||
- buf.build/grpc-ecosystem/grpc-gateway
|
|
||||||
breaking:
|
breaking:
|
||||||
use:
|
use:
|
||||||
- FILE
|
- FILE
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,9 @@ package concord.call.v1;
|
||||||
|
|
||||||
option go_package = "github.com/Alexander-D-Karpov/concord/api/gen/go/call/v1;callv1";
|
option go_package = "github.com/Alexander-D-Karpov/concord/api/gen/go/call/v1;callv1";
|
||||||
|
|
||||||
import "google/api/annotations.proto";
|
|
||||||
import "google/protobuf/timestamp.proto";
|
|
||||||
|
|
||||||
message JoinVoiceRequest {
|
message JoinVoiceRequest {
|
||||||
string room_id = 1;
|
string room_id = 1;
|
||||||
bool audio_only = 2;
|
bool audio_only = 2;
|
||||||
string preferred_region = 3;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message UdpEndpoint {
|
message UdpEndpoint {
|
||||||
|
|
@ -37,7 +33,6 @@ message JoinVoiceResponse {
|
||||||
CodecHint codec = 4;
|
CodecHint codec = 4;
|
||||||
CryptoSuite crypto = 5;
|
CryptoSuite crypto = 5;
|
||||||
repeated Participant participants = 6;
|
repeated Participant participants = 6;
|
||||||
uint32 screen_ssrc = 7;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message Participant {
|
message Participant {
|
||||||
|
|
@ -45,9 +40,6 @@ message Participant {
|
||||||
uint32 ssrc = 2;
|
uint32 ssrc = 2;
|
||||||
bool muted = 3;
|
bool muted = 3;
|
||||||
bool video_enabled = 4;
|
bool video_enabled = 4;
|
||||||
bool screen_sharing = 5;
|
|
||||||
uint32 video_ssrc = 6;
|
|
||||||
uint32 screen_ssrc = 7;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message LeaveVoiceRequest {
|
message LeaveVoiceRequest {
|
||||||
|
|
@ -58,51 +50,13 @@ message SetMediaPrefsRequest {
|
||||||
string room_id = 1;
|
string room_id = 1;
|
||||||
bool audio_only = 2;
|
bool audio_only = 2;
|
||||||
bool video_enabled = 3;
|
bool video_enabled = 3;
|
||||||
bool screen_sharing = 4;
|
bool muted = 4;
|
||||||
bool muted = 5;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message EmptyResponse {}
|
message EmptyResponse {}
|
||||||
|
|
||||||
message GetVoiceStatusRequest {
|
|
||||||
string room_id = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message VoiceParticipant {
|
|
||||||
string user_id = 1;
|
|
||||||
bool muted = 2;
|
|
||||||
bool video_enabled = 3;
|
|
||||||
bool screen_sharing = 4;
|
|
||||||
bool speaking = 5;
|
|
||||||
google.protobuf.Timestamp joined_at = 6;
|
|
||||||
}
|
|
||||||
|
|
||||||
message GetVoiceStatusResponse {
|
|
||||||
repeated VoiceParticipant participants = 1;
|
|
||||||
int32 total_participants = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
service CallService {
|
service CallService {
|
||||||
rpc JoinVoice(JoinVoiceRequest) returns (JoinVoiceResponse) {
|
rpc JoinVoice(JoinVoiceRequest) returns (JoinVoiceResponse);
|
||||||
option (google.api.http) = {
|
rpc LeaveVoice(LeaveVoiceRequest) returns (EmptyResponse);
|
||||||
post: "/v1/rooms/{room_id}/voice/join"
|
rpc SetMediaPrefs(SetMediaPrefsRequest) returns (EmptyResponse);
|
||||||
body: "*"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
rpc LeaveVoice(LeaveVoiceRequest) returns (EmptyResponse) {
|
|
||||||
option (google.api.http) = {
|
|
||||||
post: "/v1/rooms/{room_id}/voice/leave"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
rpc SetMediaPrefs(SetMediaPrefsRequest) returns (EmptyResponse) {
|
|
||||||
option (google.api.http) = {
|
|
||||||
put: "/v1/rooms/{room_id}/voice/prefs"
|
|
||||||
body: "*"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
rpc GetVoiceStatus(GetVoiceStatusRequest) returns (GetVoiceStatusResponse) {
|
|
||||||
option (google.api.http) = {
|
|
||||||
get: "/v1/rooms/{room_id}/voice"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -21,17 +21,6 @@ message User {
|
||||||
google.protobuf.Timestamp created_at = 5;
|
google.protobuf.Timestamp created_at = 5;
|
||||||
string status = 6;
|
string status = 6;
|
||||||
string bio = 7;
|
string bio = 7;
|
||||||
string avatar_thumbnail_url = 8;
|
|
||||||
string status_preference = 9;
|
|
||||||
}
|
|
||||||
|
|
||||||
message AvatarEntry {
|
|
||||||
string id = 1;
|
|
||||||
string user_id = 2;
|
|
||||||
string full_url = 3;
|
|
||||||
string thumbnail_url = 4;
|
|
||||||
string original_filename = 5;
|
|
||||||
google.protobuf.Timestamp created_at = 6;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message Room {
|
message Room {
|
||||||
|
|
@ -51,8 +40,6 @@ message Member {
|
||||||
Role role = 3;
|
Role role = 3;
|
||||||
google.protobuf.Timestamp joined_at = 4;
|
google.protobuf.Timestamp joined_at = 4;
|
||||||
string nickname = 5;
|
string nickname = 5;
|
||||||
string status = 6;
|
|
||||||
int64 last_read_message_id = 7;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message VoiceServer {
|
message VoiceServer {
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -4,9 +4,8 @@ package concord.registry.v1;
|
||||||
|
|
||||||
option go_package = "github.com/Alexander-D-Karpov/concord/api/gen/go/registry/v1;registryv1";
|
option go_package = "github.com/Alexander-D-Karpov/concord/api/gen/go/registry/v1;registryv1";
|
||||||
|
|
||||||
import "google/api/annotations.proto";
|
|
||||||
import "google/protobuf/timestamp.proto";
|
|
||||||
import "common/v1/types.proto";
|
import "common/v1/types.proto";
|
||||||
|
import "google/protobuf/timestamp.proto";
|
||||||
|
|
||||||
message RegisterServerRequest {
|
message RegisterServerRequest {
|
||||||
concord.common.v1.VoiceServer server = 1;
|
concord.common.v1.VoiceServer server = 1;
|
||||||
|
|
@ -38,21 +37,7 @@ message ListServersResponse {
|
||||||
message EmptyResponse {}
|
message EmptyResponse {}
|
||||||
|
|
||||||
service RegistryService {
|
service RegistryService {
|
||||||
rpc RegisterServer(RegisterServerRequest) returns (RegisterServerResponse) {
|
rpc RegisterServer(RegisterServerRequest) returns (RegisterServerResponse);
|
||||||
option (google.api.http) = {
|
rpc Heartbeat(HeartbeatRequest) returns (EmptyResponse);
|
||||||
post: "/v1/registry/servers"
|
rpc ListServers(ListServersRequest) returns (ListServersResponse);
|
||||||
body: "*"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
rpc Heartbeat(HeartbeatRequest) returns (EmptyResponse) {
|
|
||||||
option (google.api.http) = {
|
|
||||||
post: "/v1/registry/heartbeat"
|
|
||||||
body: "*"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
rpc ListServers(ListServersRequest) returns (ListServersResponse) {
|
|
||||||
option (google.api.http) = {
|
|
||||||
get: "/v1/registry/servers"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -4,8 +4,6 @@ package concord.rooms.v1;
|
||||||
|
|
||||||
option go_package = "github.com/Alexander-D-Karpov/concord/api/gen/go/rooms/v1;roomsv1";
|
option go_package = "github.com/Alexander-D-Karpov/concord/api/gen/go/rooms/v1;roomsv1";
|
||||||
|
|
||||||
import "google/api/annotations.proto";
|
|
||||||
import "google/protobuf/wrappers.proto";
|
|
||||||
import "common/v1/types.proto";
|
import "common/v1/types.proto";
|
||||||
|
|
||||||
message CreateRoomRequest {
|
message CreateRoomRequest {
|
||||||
|
|
@ -22,9 +20,9 @@ message GetRoomRequest {
|
||||||
|
|
||||||
message UpdateRoomRequest {
|
message UpdateRoomRequest {
|
||||||
string room_id = 1;
|
string room_id = 1;
|
||||||
google.protobuf.StringValue name = 2;
|
string name = 2;
|
||||||
google.protobuf.StringValue description = 3;
|
string description = 3;
|
||||||
google.protobuf.BoolValue is_private = 4;
|
bool is_private = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
message ListRoomsForUserRequest {}
|
message ListRoomsForUserRequest {}
|
||||||
|
|
@ -45,37 +43,10 @@ message DeleteRoomRequest {
|
||||||
message EmptyResponse {}
|
message EmptyResponse {}
|
||||||
|
|
||||||
service RoomsService {
|
service RoomsService {
|
||||||
rpc CreateRoom(CreateRoomRequest) returns (concord.common.v1.Room) {
|
rpc CreateRoom(CreateRoomRequest) returns (concord.common.v1.Room);
|
||||||
option (google.api.http) = {
|
rpc GetRoom(GetRoomRequest) returns (concord.common.v1.Room);
|
||||||
post: "/v1/rooms"
|
rpc UpdateRoom(UpdateRoomRequest) returns (concord.common.v1.Room);
|
||||||
body: "*"
|
rpc ListRoomsForUser(ListRoomsForUserRequest) returns (ListRoomsForUserResponse);
|
||||||
};
|
rpc AttachVoiceServer(AttachVoiceServerRequest) returns (concord.common.v1.Room);
|
||||||
}
|
rpc DeleteRoom(DeleteRoomRequest) returns (EmptyResponse);
|
||||||
rpc GetRoom(GetRoomRequest) returns (concord.common.v1.Room) {
|
|
||||||
option (google.api.http) = {
|
|
||||||
get: "/v1/rooms/{room_id}"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
rpc UpdateRoom(UpdateRoomRequest) returns (concord.common.v1.Room) {
|
|
||||||
option (google.api.http) = {
|
|
||||||
patch: "/v1/rooms/{room_id}"
|
|
||||||
body: "*"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
rpc ListRoomsForUser(ListRoomsForUserRequest) returns (ListRoomsForUserResponse) {
|
|
||||||
option (google.api.http) = {
|
|
||||||
get: "/v1/rooms"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
rpc AttachVoiceServer(AttachVoiceServerRequest) returns (concord.common.v1.Room) {
|
|
||||||
option (google.api.http) = {
|
|
||||||
post: "/v1/rooms/{room_id}/voice-server"
|
|
||||||
body: "*"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
rpc DeleteRoom(DeleteRoomRequest) returns (EmptyResponse) {
|
|
||||||
option (google.api.http) = {
|
|
||||||
delete: "/v1/rooms/{room_id}"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,28 +0,0 @@
|
||||||
syntax = "proto3";
|
|
||||||
|
|
||||||
package concord.unfurl.v1;
|
|
||||||
|
|
||||||
option go_package = "github.com/Alexander-D-Karpov/concord/api/gen/go/unfurl/v1;unfurlv1";
|
|
||||||
|
|
||||||
import "google/api/annotations.proto";
|
|
||||||
|
|
||||||
message UnfurlRequest {
|
|
||||||
string url = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message UnfurlResponse {
|
|
||||||
string url = 1;
|
|
||||||
string title = 2;
|
|
||||||
string description = 3;
|
|
||||||
string image = 4;
|
|
||||||
string site_name = 5;
|
|
||||||
string favicon = 6;
|
|
||||||
}
|
|
||||||
|
|
||||||
service UnfurlService {
|
|
||||||
rpc Unfurl(UnfurlRequest) returns (UnfurlResponse) {
|
|
||||||
option (google.api.http) = {
|
|
||||||
get: "/v1/unfurl"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -4,7 +4,6 @@ package concord.users.v1;
|
||||||
|
|
||||||
option go_package = "github.com/Alexander-D-Karpov/concord/api/gen/go/users/v1;usersv1";
|
option go_package = "github.com/Alexander-D-Karpov/concord/api/gen/go/users/v1;usersv1";
|
||||||
|
|
||||||
import "google/api/annotations.proto";
|
|
||||||
import "common/v1/types.proto";
|
import "common/v1/types.proto";
|
||||||
|
|
||||||
message GetSelfRequest {}
|
message GetSelfRequest {}
|
||||||
|
|
@ -47,84 +46,12 @@ message ListUsersByIDsResponse {
|
||||||
repeated concord.common.v1.User users = 1;
|
repeated concord.common.v1.User users = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
message UploadAvatarRequest {
|
|
||||||
bytes image_data = 1;
|
|
||||||
string filename = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message UploadAvatarResponse {
|
|
||||||
string avatar_url = 1;
|
|
||||||
string thumbnail_url = 2;
|
|
||||||
concord.common.v1.AvatarEntry avatar = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
message DeleteAvatarRequest {
|
|
||||||
string avatar_id = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message DeleteAvatarResponse {}
|
|
||||||
|
|
||||||
message GetAvatarHistoryRequest {
|
|
||||||
string user_id = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message GetAvatarHistoryResponse {
|
|
||||||
repeated concord.common.v1.AvatarEntry avatars = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
service UsersService {
|
service UsersService {
|
||||||
rpc GetSelf(GetSelfRequest) returns (concord.common.v1.User) {
|
rpc GetSelf(GetSelfRequest) returns (concord.common.v1.User);
|
||||||
option (google.api.http) = {
|
rpc GetUser(GetUserRequest) returns (concord.common.v1.User);
|
||||||
get: "/v1/users/me"
|
rpc GetUserByHandle(GetUserByHandleRequest) returns (concord.common.v1.User);
|
||||||
};
|
rpc UpdateProfile(UpdateProfileRequest) returns (concord.common.v1.User);
|
||||||
}
|
rpc UpdateStatus(UpdateStatusRequest) returns (concord.common.v1.User);
|
||||||
rpc GetUser(GetUserRequest) returns (concord.common.v1.User) {
|
rpc SearchUsers(SearchUsersRequest) returns (SearchUsersResponse);
|
||||||
option (google.api.http) = {
|
rpc ListUsersByIDs(ListUsersByIDsRequest) returns (ListUsersByIDsResponse);
|
||||||
get: "/v1/users/{user_id}"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
rpc GetUserByHandle(GetUserByHandleRequest) returns (concord.common.v1.User) {
|
|
||||||
option (google.api.http) = {
|
|
||||||
get: "/v1/users/handle/{handle}"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
rpc UpdateProfile(UpdateProfileRequest) returns (concord.common.v1.User) {
|
|
||||||
option (google.api.http) = {
|
|
||||||
patch: "/v1/users/me"
|
|
||||||
body: "*"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
rpc UpdateStatus(UpdateStatusRequest) returns (concord.common.v1.User) {
|
|
||||||
option (google.api.http) = {
|
|
||||||
put: "/v1/users/me/status"
|
|
||||||
body: "*"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
rpc SearchUsers(SearchUsersRequest) returns (SearchUsersResponse) {
|
|
||||||
option (google.api.http) = {
|
|
||||||
get: "/v1/users/search"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
rpc ListUsersByIDs(ListUsersByIDsRequest) returns (ListUsersByIDsResponse) {
|
|
||||||
option (google.api.http) = {
|
|
||||||
post: "/v1/users/batch"
|
|
||||||
body: "*"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
rpc UploadAvatar(UploadAvatarRequest) returns (UploadAvatarResponse) {
|
|
||||||
option (google.api.http) = {
|
|
||||||
post: "/v1/users/me/avatar"
|
|
||||||
body: "*"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
rpc DeleteAvatar(DeleteAvatarRequest) returns (DeleteAvatarResponse) {
|
|
||||||
option (google.api.http) = {
|
|
||||||
delete: "/v1/users/me/avatar/{avatar_id}"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
rpc GetAvatarHistory(GetAvatarHistoryRequest) returns (GetAvatarHistoryResponse) {
|
|
||||||
option (google.api.http) = {
|
|
||||||
get: "/v1/users/{user_id}/avatars"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -1,16 +1,8 @@
|
||||||
# Build stage
|
FROM golang:1.23-alpine AS builder
|
||||||
FROM golang:1.25-alpine AS builder
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
RUN apk add --no-cache git make protobuf
|
RUN apk add --no-cache git make protobuf-dev
|
||||||
|
|
||||||
ENV PATH="/go/bin:${PATH}"
|
WORKDIR /build
|
||||||
|
|
||||||
RUN CGO_ENABLED=0 go install github.com/bufbuild/buf/cmd/buf@v1.28.1 \
|
|
||||||
&& go install google.golang.org/protobuf/cmd/protoc-gen-go@latest \
|
|
||||||
&& go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest \
|
|
||||||
&& go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway@latest \
|
|
||||||
&& go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2@latest
|
|
||||||
|
|
||||||
COPY go.mod go.sum ./
|
COPY go.mod go.sum ./
|
||||||
RUN go mod download
|
RUN go mod download
|
||||||
|
|
@ -18,16 +10,22 @@ RUN go mod download
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
RUN make proto
|
RUN make proto
|
||||||
|
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o concord-api ./cmd/concord-api
|
||||||
|
|
||||||
RUN go build -o /bin/concord-api ./cmd/concord-api
|
FROM alpine:latest
|
||||||
|
|
||||||
|
RUN apk --no-cache add ca-certificates tzdata
|
||||||
|
|
||||||
# Final stage
|
|
||||||
FROM alpine:3.19
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
RUN apk add --no-cache ca-certificates
|
|
||||||
|
|
||||||
COPY --from=builder /bin/concord-api /app/concord-api
|
COPY --from=builder /build/concord-api .
|
||||||
COPY --from=builder /app/api/gen/openapiv2/ /app/api/gen/openapiv2/
|
|
||||||
|
|
||||||
EXPOSE 8080 9000 9100 8081
|
RUN addgroup -g 1000 concord && \
|
||||||
CMD ["/app/concord-api"]
|
adduser -D -u 1000 -G concord concord && \
|
||||||
|
chown -R concord:concord /app
|
||||||
|
|
||||||
|
USER concord
|
||||||
|
|
||||||
|
EXPOSE 9090
|
||||||
|
|
||||||
|
ENTRYPOINT ["./concord-api"]
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,8 @@
|
||||||
# Build stage
|
FROM golang:1.23-alpine AS builder
|
||||||
FROM golang:1.25-alpine AS builder
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
RUN apk add --no-cache git make protobuf
|
RUN apk add --no-cache git make protobuf-dev
|
||||||
ENV PATH="/go/bin:${PATH}"
|
|
||||||
|
|
||||||
RUN CGO_ENABLED=0 go install github.com/bufbuild/buf/cmd/buf@v1.28.1 \
|
WORKDIR /build
|
||||||
&& go install google.golang.org/protobuf/cmd/protoc-gen-go@latest \
|
|
||||||
&& go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest \
|
|
||||||
&& go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway@latest \
|
|
||||||
&& go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2@latest
|
|
||||||
|
|
||||||
COPY go.mod go.sum ./
|
COPY go.mod go.sum ./
|
||||||
RUN go mod download
|
RUN go mod download
|
||||||
|
|
@ -17,15 +10,23 @@ RUN go mod download
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
RUN make proto
|
RUN make proto
|
||||||
|
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o concord-voice ./cmd/concord-voice
|
||||||
|
|
||||||
RUN go build -o /bin/concord-voice ./cmd/concord-voice
|
FROM alpine:latest
|
||||||
|
|
||||||
|
RUN apk --no-cache add ca-certificates
|
||||||
|
|
||||||
# Final stage
|
|
||||||
FROM alpine:3.19
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
RUN apk add --no-cache ca-certificates
|
|
||||||
|
|
||||||
COPY --from=builder /bin/concord-voice /app/concord-voice
|
COPY --from=builder /build/concord-voice .
|
||||||
|
|
||||||
EXPOSE 50000-50049/udp 9001 9101 8082
|
RUN addgroup -g 1000 concord && \
|
||||||
CMD ["/app/concord-voice"]
|
adduser -D -u 1000 -G concord concord && \
|
||||||
|
chown -R concord:concord /app
|
||||||
|
|
||||||
|
USER concord
|
||||||
|
|
||||||
|
EXPOSE 50000-52000/udp
|
||||||
|
EXPOSE 9091
|
||||||
|
|
||||||
|
ENTRYPOINT ["./concord-voice"]
|
||||||
|
|
|
||||||
|
|
@ -1,94 +0,0 @@
|
||||||
server {
|
|
||||||
listen 80;
|
|
||||||
server_name concord.akarpov.ru;
|
|
||||||
|
|
||||||
location /.well-known/acme-challenge/ {
|
|
||||||
root /var/www/html;
|
|
||||||
}
|
|
||||||
|
|
||||||
location / {
|
|
||||||
return 301 https://$host$request_uri;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
server {
|
|
||||||
listen 443 ssl;
|
|
||||||
http2 on;
|
|
||||||
server_name concord.akarpov.ru;
|
|
||||||
|
|
||||||
ssl_certificate /etc/letsencrypt/live/concord.akarpov.ru/fullchain.pem;
|
|
||||||
ssl_certificate_key /etc/letsencrypt/live/concord.akarpov.ru/privkey.pem;
|
|
||||||
|
|
||||||
include /etc/letsencrypt/options-ssl-nginx.conf;
|
|
||||||
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
|
|
||||||
|
|
||||||
client_max_body_size 100m;
|
|
||||||
|
|
||||||
location ^~ /files/avatars/ {
|
|
||||||
alias /var/www/media/concord/avatars/;
|
|
||||||
expires 365d;
|
|
||||||
add_header Cache-Control "public, max-age=31536000, immutable";
|
|
||||||
access_log off;
|
|
||||||
}
|
|
||||||
|
|
||||||
location ^~ /files/ {
|
|
||||||
alias /var/www/media/concord/;
|
|
||||||
expires 7d;
|
|
||||||
add_header Cache-Control "public";
|
|
||||||
access_log off;
|
|
||||||
}
|
|
||||||
|
|
||||||
location ^~ /concord. {
|
|
||||||
grpc_pass grpc://127.0.0.1:19000;
|
|
||||||
|
|
||||||
grpc_read_timeout 1h;
|
|
||||||
grpc_send_timeout 1h;
|
|
||||||
|
|
||||||
grpc_set_header Host $host;
|
|
||||||
grpc_set_header X-Real-IP $remote_addr;
|
|
||||||
grpc_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
grpc_set_header X-Forwarded-Proto https;
|
|
||||||
}
|
|
||||||
|
|
||||||
location ^~ /grpc. {
|
|
||||||
grpc_pass grpc://127.0.0.1:19000;
|
|
||||||
|
|
||||||
grpc_read_timeout 1h;
|
|
||||||
grpc_send_timeout 1h;
|
|
||||||
|
|
||||||
grpc_set_header Host $host;
|
|
||||||
grpc_set_header X-Real-IP $remote_addr;
|
|
||||||
grpc_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
grpc_set_header X-Forwarded-Proto https;
|
|
||||||
}
|
|
||||||
|
|
||||||
location / {
|
|
||||||
proxy_pass http://127.0.0.1:18080;
|
|
||||||
|
|
||||||
proxy_http_version 1.1;
|
|
||||||
proxy_set_header Host $host;
|
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
proxy_set_header X-Forwarded-Proto https;
|
|
||||||
|
|
||||||
proxy_read_timeout 300s;
|
|
||||||
proxy_send_timeout 300s;
|
|
||||||
}
|
|
||||||
|
|
||||||
location ^~ /docs {
|
|
||||||
proxy_pass http://127.0.0.1:18080;
|
|
||||||
proxy_http_version 1.1;
|
|
||||||
proxy_set_header Host $host;
|
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
proxy_set_header X-Forwarded-Proto https;
|
|
||||||
}
|
|
||||||
|
|
||||||
location = /version {
|
|
||||||
proxy_pass http://127.0.0.1:18080;
|
|
||||||
proxy_http_version 1.1;
|
|
||||||
proxy_set_header Host $host;
|
|
||||||
}
|
|
||||||
location /metrics { return 404; }
|
|
||||||
location /health { return 404; }
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
7
go.mod
7
go.mod
|
|
@ -13,10 +13,8 @@ require (
|
||||||
github.com/stretchr/testify v1.11.1
|
github.com/stretchr/testify v1.11.1
|
||||||
go.uber.org/zap v1.27.0
|
go.uber.org/zap v1.27.0
|
||||||
golang.org/x/crypto v0.43.0
|
golang.org/x/crypto v0.43.0
|
||||||
golang.org/x/image v0.36.0
|
|
||||||
golang.org/x/oauth2 v0.32.0
|
golang.org/x/oauth2 v0.32.0
|
||||||
golang.org/x/time v0.14.0
|
golang.org/x/time v0.14.0
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20250929231259-57b25ae835d4
|
|
||||||
google.golang.org/grpc v1.76.0
|
google.golang.org/grpc v1.76.0
|
||||||
google.golang.org/protobuf v1.36.10
|
google.golang.org/protobuf v1.36.10
|
||||||
)
|
)
|
||||||
|
|
@ -37,9 +35,10 @@ require (
|
||||||
go.uber.org/multierr v1.10.0 // indirect
|
go.uber.org/multierr v1.10.0 // indirect
|
||||||
go.yaml.in/yaml/v2 v2.4.2 // indirect
|
go.yaml.in/yaml/v2 v2.4.2 // indirect
|
||||||
golang.org/x/net v0.45.0 // indirect
|
golang.org/x/net v0.45.0 // indirect
|
||||||
golang.org/x/sync v0.19.0 // indirect
|
golang.org/x/sync v0.17.0 // indirect
|
||||||
golang.org/x/sys v0.37.0 // indirect
|
golang.org/x/sys v0.37.0 // indirect
|
||||||
golang.org/x/text v0.34.0 // indirect
|
golang.org/x/text v0.30.0 // indirect
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20250929231259-57b25ae835d4 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250929231259-57b25ae835d4 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250929231259-57b25ae835d4 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|
|
||||||
10
go.sum
10
go.sum
|
|
@ -86,18 +86,16 @@ go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
|
||||||
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
|
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
|
||||||
golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
|
golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
|
||||||
golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
|
golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
|
||||||
golang.org/x/image v0.36.0 h1:Iknbfm1afbgtwPTmHnS2gTM/6PPZfH+z2EFuOkSbqwc=
|
|
||||||
golang.org/x/image v0.36.0/go.mod h1:YsWD2TyyGKiIX1kZlu9QfKIsQ4nAAK9bdgdrIsE7xy4=
|
|
||||||
golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM=
|
golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM=
|
||||||
golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
|
golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
|
||||||
golang.org/x/oauth2 v0.32.0 h1:jsCblLleRMDrxMN29H3z/k1KliIvpLgCkE6R8FXXNgY=
|
golang.org/x/oauth2 v0.32.0 h1:jsCblLleRMDrxMN29H3z/k1KliIvpLgCkE6R8FXXNgY=
|
||||||
golang.org/x/oauth2 v0.32.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
golang.org/x/oauth2 v0.32.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
|
||||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||||
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
|
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
|
||||||
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
|
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
|
||||||
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
|
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
|
||||||
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
|
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
|
||||||
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
|
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
|
||||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||||
|
|
|
||||||
60
go.work.sum
60
go.work.sum
|
|
@ -1,60 +0,0 @@
|
||||||
cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY=
|
|
||||||
cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw=
|
|
||||||
cloud.google.com/go/compute/metadata v0.7.0 h1:PBWF+iiAerVNe8UCHxdOt6eHLVc3ydFeOCw78U8ytSU=
|
|
||||||
cloud.google.com/go/compute/metadata v0.7.0/go.mod h1:j5MvL9PprKL39t166CoB1uVHfQMs4tFQZZcKwksXUjo=
|
|
||||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.29.0 h1:UQUsRi8WTzhZntp5313l+CHIAT95ojUI2lpP/ExlZa4=
|
|
||||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.29.0/go.mod h1:Cz6ft6Dkn3Et6l2v2a9/RpN7epQ1GtDlO6lj8bEcOvw=
|
|
||||||
github.com/alecthomas/kingpin/v2 v2.4.0 h1:f48lwail6p8zpO1bC4TxtqACaGqHYA22qkHjHpqDjYY=
|
|
||||||
github.com/alecthomas/kingpin/v2 v2.4.0/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE=
|
|
||||||
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc=
|
|
||||||
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
|
|
||||||
github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg=
|
|
||||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
|
||||||
github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 h1:aQ3y1lwWyqYPiWZThqv1aFbZMiM9vblcSArJRf2Irls=
|
|
||||||
github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
|
|
||||||
github.com/envoyproxy/go-control-plane v0.13.4 h1:zEqyPVyku6IvWCFwux4x9RxkLOMUL+1vC9xUFv5l2/M=
|
|
||||||
github.com/envoyproxy/go-control-plane v0.13.4/go.mod h1:kDfuBlDVsSj2MjrLEtRWtHlsWIFcGyB2RMO44Dc5GZA=
|
|
||||||
github.com/envoyproxy/go-control-plane/envoy v1.32.4 h1:jb83lalDRZSpPWW2Z7Mck/8kXZ5CQAFYVjQcdVIr83A=
|
|
||||||
github.com/envoyproxy/go-control-plane/envoy v1.32.4/go.mod h1:Gzjc5k8JcJswLjAx1Zm+wSYE20UrLtt7JZMWiWQXQEw=
|
|
||||||
github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 h1:/G9QYbddjL25KvtKTv3an9lx6VBE2cnb8wp1vEGNYGI=
|
|
||||||
github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4=
|
|
||||||
github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8=
|
|
||||||
github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU=
|
|
||||||
github.com/go-jose/go-jose/v4 v4.1.2 h1:TK/7NqRQZfgAh+Td8AlsrvtPoUyiHh0LqVvokh+1vHI=
|
|
||||||
github.com/go-jose/go-jose/v4 v4.1.2/go.mod h1:22cg9HWM1pOlnRiY+9cQYJ9XHmya1bYW8OeDM6Ku6Oo=
|
|
||||||
github.com/golang/glog v1.2.5 h1:DrW6hGnjIhtvhOIiAKT6Psh/Kd/ldepEa81DKeiRJ5I=
|
|
||||||
github.com/golang/glog v1.2.5/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
|
|
||||||
github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
|
|
||||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
|
||||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
|
||||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
|
||||||
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
|
|
||||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
|
||||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
|
||||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
|
||||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU=
|
|
||||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
|
||||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo=
|
|
||||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
|
|
||||||
github.com/rogpeppe/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi2s=
|
|
||||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
|
||||||
github.com/spiffe/go-spiffe/v2 v2.5.0 h1:N2I01KCUkv1FAjZXJMwh95KK1ZIQLYbPfhaxw8WS0hE=
|
|
||||||
github.com/spiffe/go-spiffe/v2 v2.5.0/go.mod h1:P+NxobPc6wXhVtINNtFjNWGBTreew1GBUCwT2wPmb7g=
|
|
||||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
|
||||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
|
||||||
github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc=
|
|
||||||
github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU=
|
|
||||||
github.com/zeebo/errs v1.4.0 h1:XNdoD/RRMKP7HD0UhJnIzUy74ISdGGxURlYG8HSWSfM=
|
|
||||||
github.com/zeebo/errs v1.4.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4=
|
|
||||||
go.opentelemetry.io/contrib/detectors/gcp v1.36.0 h1:F7q2tNlCaHY9nMKHR6XH9/qkp8FktLnIcy6jJNyOCQw=
|
|
||||||
go.opentelemetry.io/contrib/detectors/gcp v1.36.0/go.mod h1:IbBN8uAIIx734PTonTPxAxnjc2pQTxWNkwfstZ+6H2k=
|
|
||||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
|
||||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
|
||||||
golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c=
|
|
||||||
golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU=
|
|
||||||
golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q=
|
|
||||||
golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss=
|
|
||||||
golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc=
|
|
||||||
golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg=
|
|
||||||
|
|
@ -50,8 +50,6 @@ func (s *Service) KickUser(ctx context.Context, adminUserID, roomID, targetUserI
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
s.hub.NotifyRoomLeave(targetUserID, roomID)
|
|
||||||
|
|
||||||
s.hub.BroadcastToRoom(roomID, &streamv1.ServerEvent{
|
s.hub.BroadcastToRoom(roomID, &streamv1.ServerEvent{
|
||||||
EventId: uuid.New().String(),
|
EventId: uuid.New().String(),
|
||||||
CreatedAt: timestamppb.Now(),
|
CreatedAt: timestamppb.Now(),
|
||||||
|
|
@ -116,8 +114,6 @@ func (s *Service) BanUser(ctx context.Context, adminUserID, roomID, targetUserID
|
||||||
s.logger.Warn("failed to remove member during ban", zap.Error(err))
|
s.logger.Warn("failed to remove member during ban", zap.Error(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
s.hub.NotifyRoomLeave(targetUserID, roomID)
|
|
||||||
|
|
||||||
s.hub.BroadcastToRoom(roomID, &streamv1.ServerEvent{
|
s.hub.BroadcastToRoom(roomID, &streamv1.ServerEvent{
|
||||||
EventId: uuid.New().String(),
|
EventId: uuid.New().String(),
|
||||||
CreatedAt: timestamppb.Now(),
|
CreatedAt: timestamppb.Now(),
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -14,8 +14,6 @@ type Config struct {
|
||||||
Logging LoggingConfig
|
Logging LoggingConfig
|
||||||
Redis RedisConfig
|
Redis RedisConfig
|
||||||
RateLimit RateLimitConfig
|
RateLimit RateLimitConfig
|
||||||
Storage StorageConfig
|
|
||||||
Email EmailConfig
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ServerConfig struct {
|
type ServerConfig struct {
|
||||||
|
|
@ -25,8 +23,6 @@ type ServerConfig struct {
|
||||||
ReadTimeout time.Duration
|
ReadTimeout time.Duration
|
||||||
WriteTimeout time.Duration
|
WriteTimeout time.Duration
|
||||||
IdleTimeout time.Duration
|
IdleTimeout time.Duration
|
||||||
TLSCertFile string
|
|
||||||
TLSKeyFile string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type DatabaseConfig struct {
|
type DatabaseConfig struct {
|
||||||
|
|
@ -63,14 +59,12 @@ type VoiceConfig struct {
|
||||||
UDPHost string
|
UDPHost string
|
||||||
UDPPortStart int
|
UDPPortStart int
|
||||||
UDPPortEnd int
|
UDPPortEnd int
|
||||||
UDPPortCount int
|
|
||||||
ControlPort int
|
ControlPort int
|
||||||
ServerID string
|
ServerID string
|
||||||
Region string
|
Region string
|
||||||
Secret string
|
Secret string
|
||||||
RegistryURL string
|
RegistryURL string
|
||||||
PublicHost string
|
PublicHost string
|
||||||
StatusPort int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type LoggingConfig struct {
|
type LoggingConfig struct {
|
||||||
|
|
@ -89,25 +83,10 @@ type RedisConfig struct {
|
||||||
Enabled bool
|
Enabled bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type StorageConfig struct {
|
|
||||||
Path string
|
|
||||||
URL string
|
|
||||||
}
|
|
||||||
|
|
||||||
type RateLimitConfig struct {
|
type RateLimitConfig struct {
|
||||||
Enabled bool
|
Enabled bool
|
||||||
RequestsPerMinute int
|
RequestsPerMinute int
|
||||||
Burst int
|
Burst int
|
||||||
BypassToken string
|
|
||||||
}
|
|
||||||
|
|
||||||
type EmailConfig struct {
|
|
||||||
SMTPHost string
|
|
||||||
SMTPPort int
|
|
||||||
Username string
|
|
||||||
Password string
|
|
||||||
FromAddress string
|
|
||||||
FromName string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Load() (*Config, error) {
|
func Load() (*Config, error) {
|
||||||
|
|
@ -119,8 +98,6 @@ func Load() (*Config, error) {
|
||||||
ReadTimeout: getEnvDuration("READ_TIMEOUT", 10*time.Second),
|
ReadTimeout: getEnvDuration("READ_TIMEOUT", 10*time.Second),
|
||||||
WriteTimeout: getEnvDuration("WRITE_TIMEOUT", 10*time.Second),
|
WriteTimeout: getEnvDuration("WRITE_TIMEOUT", 10*time.Second),
|
||||||
IdleTimeout: getEnvDuration("IDLE_TIMEOUT", 120*time.Second),
|
IdleTimeout: getEnvDuration("IDLE_TIMEOUT", 120*time.Second),
|
||||||
TLSCertFile: getEnv("TLS_CERT_FILE", ""),
|
|
||||||
TLSKeyFile: getEnv("TLS_KEY_FILE", ""),
|
|
||||||
},
|
},
|
||||||
Database: DatabaseConfig{
|
Database: DatabaseConfig{
|
||||||
Host: getEnv("DB_HOST", "localhost"),
|
Host: getEnv("DB_HOST", "localhost"),
|
||||||
|
|
@ -145,14 +122,12 @@ func Load() (*Config, error) {
|
||||||
UDPHost: getEnv("VOICE_UDP_HOST", "0.0.0.0"),
|
UDPHost: getEnv("VOICE_UDP_HOST", "0.0.0.0"),
|
||||||
UDPPortStart: getEnvInt("VOICE_UDP_PORT_START", 50000),
|
UDPPortStart: getEnvInt("VOICE_UDP_PORT_START", 50000),
|
||||||
UDPPortEnd: getEnvInt("VOICE_UDP_PORT_END", 52000),
|
UDPPortEnd: getEnvInt("VOICE_UDP_PORT_END", 52000),
|
||||||
UDPPortCount: getEnvInt("VOICE_UDP_PORT_COUNT", 50),
|
|
||||||
ControlPort: getEnvInt("VOICE_CONTROL_PORT", 9091),
|
ControlPort: getEnvInt("VOICE_CONTROL_PORT", 9091),
|
||||||
ServerID: getEnv("VOICE_SERVER_ID", ""),
|
ServerID: getEnv("VOICE_SERVER_ID", ""),
|
||||||
Region: getEnv("VOICE_REGION", "ru-west"),
|
Region: getEnv("VOICE_REGION", "ru-west"),
|
||||||
Secret: getEnv("VOICE_SECRET", "change-me-voice-server-secret"),
|
Secret: getEnv("VOICE_SECRET", "change-me-voice-server-secret"),
|
||||||
RegistryURL: getEnv("REGISTRY_URL", "localhost:9090"),
|
RegistryURL: getEnv("REGISTRY_URL", "localhost:9090"),
|
||||||
PublicHost: getEnv("VOICE_PUBLIC_HOST", "localhost"),
|
PublicHost: getEnv("VOICE_PUBLIC_HOST", "localhost"),
|
||||||
StatusPort: getEnvInt("VOICE_STATUS_PORT", 9092),
|
|
||||||
},
|
},
|
||||||
Logging: LoggingConfig{
|
Logging: LoggingConfig{
|
||||||
Level: getEnv("LOG_LEVEL", "info"),
|
Level: getEnv("LOG_LEVEL", "info"),
|
||||||
|
|
@ -172,17 +147,6 @@ func Load() (*Config, error) {
|
||||||
Enabled: getEnvBool("RATE_LIMIT_ENABLED", true),
|
Enabled: getEnvBool("RATE_LIMIT_ENABLED", true),
|
||||||
RequestsPerMinute: getEnvInt("RATE_LIMIT_REQUESTS_PER_MINUTE", 60),
|
RequestsPerMinute: getEnvInt("RATE_LIMIT_REQUESTS_PER_MINUTE", 60),
|
||||||
Burst: getEnvInt("RATE_LIMIT_BURST", 10),
|
Burst: getEnvInt("RATE_LIMIT_BURST", 10),
|
||||||
BypassToken: getEnv("RATE_LIMIT_BYPASS_TOKEN", ""),
|
|
||||||
},
|
|
||||||
Storage: StorageConfig{
|
|
||||||
Path: getEnv("STORAGE_PATH", "./uploads"),
|
|
||||||
URL: getEnv("STORAGE_URL", "/files"),
|
|
||||||
},
|
|
||||||
Email: EmailConfig{
|
|
||||||
SMTPHost: getEnv("EMAIL_SMTP_HOST", "smtp.example.com"),
|
|
||||||
SMTPPort: getEnvInt("EMAIL_SMTP_PORT", 587),
|
|
||||||
Username: getEnv("EMAIL_USERNAME", ""),
|
|
||||||
Password: getEnv("EMAIL_PASSWORD", ""),
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return cfg, nil
|
return cfg, nil
|
||||||
|
|
|
||||||
|
|
@ -107,29 +107,5 @@ func ToGRPCError(err error) error {
|
||||||
return appErr.GRPCStatus().Err()
|
return appErr.GRPCStatus().Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
if st, ok := status.FromError(err); ok {
|
|
||||||
return st.Err()
|
|
||||||
}
|
|
||||||
|
|
||||||
return status.Error(codes.Internal, err.Error())
|
return status.Error(codes.Internal, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsNotFound(err error) bool {
|
|
||||||
if err == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
var appErr *AppError
|
|
||||||
if errors.As(err, &appErr) {
|
|
||||||
if appErr.Code == codes.NotFound {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return errors.Is(appErr.Err, ErrNotFound)
|
|
||||||
}
|
|
||||||
|
|
||||||
if st, ok := status.FromError(err); ok {
|
|
||||||
return st.Code() == codes.NotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
return errors.Is(err, ErrNotFound)
|
|
||||||
}
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -7,7 +7,6 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
@ -23,23 +22,22 @@ type Advertised struct {
|
||||||
func ComputeAdvertised(ctx context.Context, userConfiguredHost, udpBindHost string, port int) Advertised {
|
func ComputeAdvertised(ctx context.Context, userConfiguredHost, udpBindHost string, port int) Advertised {
|
||||||
adv := Advertised{Port: port}
|
adv := Advertised{Port: port}
|
||||||
|
|
||||||
|
// 0) If user configured a public host (config wins)
|
||||||
if h := strings.TrimSpace(userConfiguredHost); h != "" {
|
if h := strings.TrimSpace(userConfiguredHost); h != "" {
|
||||||
h = trimScheme(h)
|
adv.PublicHost = trimScheme(h)
|
||||||
h = stripPort(h)
|
|
||||||
adv.PublicHost = h
|
|
||||||
adv.Source = "config"
|
adv.Source = "config"
|
||||||
} else if env := strings.TrimSpace(os.Getenv("CONCORD_PUBLIC_HOST")); env != "" {
|
} else if env := strings.TrimSpace(os.Getenv("CONCORD_PUBLIC_HOST")); env != "" {
|
||||||
h := trimScheme(env)
|
adv.PublicHost = trimScheme(env)
|
||||||
h = stripPort(h)
|
|
||||||
adv.PublicHost = h
|
|
||||||
adv.Source = "env"
|
adv.Source = "env"
|
||||||
} else {
|
} else {
|
||||||
|
// 1) Try to detect public IP via HTTP (short timeouts)
|
||||||
if ip, err := detectPublicIP(ctx); err == nil && ip != "" {
|
if ip, err := detectPublicIP(ctx); err == nil && ip != "" {
|
||||||
adv.PublicHost = ip
|
adv.PublicHost = ip
|
||||||
adv.Source = "http"
|
adv.Source = "http"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 2) Determine a LAN IP fallback
|
||||||
if lan, err := detectLANIPPreferOutbound(); err == nil && lan != "" {
|
if lan, err := detectLANIPPreferOutbound(); err == nil && lan != "" {
|
||||||
adv.LANHost = lan
|
adv.LANHost = lan
|
||||||
} else if lan, err := firstPrivateIPv4(); err == nil && lan != "" {
|
} else if lan, err := firstPrivateIPv4(); err == nil && lan != "" {
|
||||||
|
|
@ -49,6 +47,7 @@ func ComputeAdvertised(ctx context.Context, userConfiguredHost, udpBindHost stri
|
||||||
adv.Notes = append(adv.Notes, "Could not find a LAN IP; falling back to 127.0.0.1.")
|
adv.Notes = append(adv.Notes, "Could not find a LAN IP; falling back to 127.0.0.1.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 3) Notes / hints
|
||||||
if adv.PublicHost == "" {
|
if adv.PublicHost == "" {
|
||||||
adv.Source = "lan"
|
adv.Source = "lan"
|
||||||
adv.Notes = append(adv.Notes,
|
adv.Notes = append(adv.Notes,
|
||||||
|
|
@ -66,21 +65,9 @@ func trimScheme(h string) string {
|
||||||
h = strings.TrimSpace(h)
|
h = strings.TrimSpace(h)
|
||||||
h = strings.TrimPrefix(h, "https://")
|
h = strings.TrimPrefix(h, "https://")
|
||||||
h = strings.TrimPrefix(h, "http://")
|
h = strings.TrimPrefix(h, "http://")
|
||||||
h = strings.TrimPrefix(h, "udp://")
|
|
||||||
h = strings.TrimPrefix(h, "tcp://")
|
|
||||||
return strings.TrimSuffix(h, "/")
|
return strings.TrimSuffix(h, "/")
|
||||||
}
|
}
|
||||||
|
|
||||||
func stripPort(hostWithPort string) string {
|
|
||||||
if idx := strings.LastIndex(hostWithPort, ":"); idx != -1 {
|
|
||||||
potentialPort := hostWithPort[idx+1:]
|
|
||||||
if _, err := strconv.Atoi(potentialPort); err == nil {
|
|
||||||
return hostWithPort[:idx]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return hostWithPort
|
|
||||||
}
|
|
||||||
|
|
||||||
func isAllInterfaces(h string) bool {
|
func isAllInterfaces(h string) bool {
|
||||||
h = strings.TrimSpace(strings.ToLower(h))
|
h = strings.TrimSpace(strings.ToLower(h))
|
||||||
return h == "" || h == "0.0.0.0" || h == "::" || h == "[::]" || h == "localhost"
|
return h == "" || h == "0.0.0.0" || h == "::" || h == "[::]" || h == "localhost"
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
6
internal/infra/cache/aside.go
vendored
6
internal/infra/cache/aside.go
vendored
|
|
@ -2,7 +2,6 @@ package cache
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -14,15 +13,14 @@ func NewAsidePattern(cache *Cache) *AsidePattern {
|
||||||
return &AsidePattern{cache: cache}
|
return &AsidePattern{cache: cache}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *AsidePattern) GetOrLoad(ctx context.Context, key string, ttl time.Duration,
|
func (a *AsidePattern) GetOrLoad(ctx context.Context, key string, ttl time.Duration, loader func() (interface{}, error)) (interface{}, error) {
|
||||||
loader func() (interface{}, error)) (interface{}, error) {
|
|
||||||
var result interface{}
|
var result interface{}
|
||||||
err := a.cache.Get(ctx, key, &result)
|
err := a.cache.Get(ctx, key, &result)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if !errors.Is(err, ErrCacheMiss) {
|
if err != ErrCacheMiss {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
34
internal/infra/cache/cache.go
vendored
34
internal/infra/cache/cache.go
vendored
|
|
@ -35,10 +35,6 @@ func New(host string, port int, password string, db int) (*Cache, error) {
|
||||||
return &Cache{client: client}, nil
|
return &Cache{client: client}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cache) Client() *redis.Client {
|
|
||||||
return c.client
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Cache) Set(ctx context.Context, key string, value interface{}, ttl time.Duration) error {
|
func (c *Cache) Set(ctx context.Context, key string, value interface{}, ttl time.Duration) error {
|
||||||
data, err := json.Marshal(value)
|
data, err := json.Marshal(value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -102,33 +98,3 @@ func (c *Cache) FlushAll(ctx context.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
var ErrCacheMiss = fmt.Errorf("cache miss")
|
var ErrCacheMiss = fmt.Errorf("cache miss")
|
||||||
|
|
||||||
func (c *Cache) DeletePattern(ctx context.Context, pattern string) error {
|
|
||||||
iter := c.client.Scan(ctx, 0, pattern, 0).Iterator()
|
|
||||||
pipe := c.client.Pipeline()
|
|
||||||
|
|
||||||
for iter.Next(ctx) {
|
|
||||||
pipe.Del(ctx, iter.Val())
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := iter.Err(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := pipe.Exec(ctx)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Cache) HSet(ctx context.Context, key string, values map[string]string, ttl time.Duration) error {
|
|
||||||
pipe := c.client.Pipeline()
|
|
||||||
for k, v := range values {
|
|
||||||
pipe.HSet(ctx, key, k, v)
|
|
||||||
}
|
|
||||||
pipe.Expire(ctx, key, ttl)
|
|
||||||
_, err := pipe.Exec(ctx)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Cache) HGetAll(ctx context.Context, key string) (map[string]string, error) {
|
|
||||||
return c.client.HGetAll(ctx, key).Result()
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
CREATE TABLE IF NOT EXISTS friend_requests (
|
|
||||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
||||||
from_user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
||||||
to_user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
||||||
status TEXT NOT NULL DEFAULT 'pending',
|
|
||||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
||||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
||||||
UNIQUE (from_user_id, to_user_id),
|
|
||||||
CHECK (from_user_id != to_user_id),
|
|
||||||
CHECK (status IN ('pending', 'accepted', 'rejected'))
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_friend_requests_from ON friend_requests(from_user_id);
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_friend_requests_to ON friend_requests(to_user_id);
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_friend_requests_status ON friend_requests(status);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS friendships (
|
|
||||||
user_id1 UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
||||||
user_id2 UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
||||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
||||||
PRIMARY KEY (user_id1, user_id2),
|
|
||||||
CHECK (user_id1 < user_id2)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_friendships_user1 ON friendships(user_id1);
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_friendships_user2 ON friendships(user_id2);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS blocked_users (
|
|
||||||
blocker_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
||||||
blocked_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
||||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
||||||
PRIMARY KEY (blocker_id, blocked_id),
|
|
||||||
CHECK (blocker_id != blocked_id)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_blocked_users_blocker ON blocked_users(blocker_id);
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_blocked_users_blocked ON blocked_users(blocked_id);
|
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_friend_requests_to_status ON friend_requests(to_user_id, status);
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_friend_requests_from_status ON friend_requests(from_user_id, status);
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_friendships_lookup ON friendships(user_id1, user_id2);
|
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_blocked_users_lookup ON blocked_users(blocker_id, blocked_id);
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
CREATE INDEX IF NOT EXISTS idx_messages_content_search
|
|
||||||
ON messages USING gin(to_tsvector('english', content));
|
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_users_search
|
|
||||||
ON users USING gin(to_tsvector('english', handle || ' ' || display_name));
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION cleanup_expired_bans() RETURNS void AS $$
|
|
||||||
BEGIN
|
|
||||||
DELETE FROM room_bans WHERE expires_at IS NOT NULL AND expires_at < NOW();
|
|
||||||
END;
|
|
||||||
$$ LANGUAGE plpgsql;
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
CREATE TABLE IF NOT EXISTS room_invites (
|
|
||||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
||||||
room_id UUID NOT NULL REFERENCES rooms(id) ON DELETE CASCADE,
|
|
||||||
invited_user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
||||||
invited_by UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
||||||
status TEXT NOT NULL DEFAULT 'pending',
|
|
||||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
||||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
||||||
UNIQUE (room_id, invited_user_id),
|
|
||||||
CHECK (invited_user_id != invited_by),
|
|
||||||
CHECK (status IN ('pending', 'accepted', 'rejected'))
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_room_invites_invited_user ON room_invites(invited_user_id);
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_room_invites_invited_by ON room_invites(invited_by);
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_room_invites_room ON room_invites(room_id);
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_room_invites_status ON room_invites(status);
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_room_invites_invited_user_status ON room_invites(invited_user_id, status);
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user