Size: 3541 bytes.


  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
// cs/apps/trycopilot.ai/api/user.cc
#include "cs/apps/trycopilot.ai/api/user.hh"

#include "cs/apps/trycopilot.ai/protos/auth.proto.hh"
#include "cs/apps/trycopilot.ai/protos/gencode/user.proto.hh"
#include "cs/apps/trycopilot.ai/protos/user.proto.hh"
#include "cs/log.hh"
#include "cs/net/http/request.hh"
#include "cs/net/http/response.hh"
#include "cs/net/http/web_app.hh"
#include "cs/net/proto/db/client.gpt.hh"
#include "cs/net/proto/db/query_helpers.gpt.hh"
#include "cs/net/proto/db/query_view.gpt.hh"
#include "cs/net/rpc/rpc.hh"
#include "cs/parsers/parsers.hh"
#include "cs/result.hh"
#include "cs/util/context.hh"
#include "cs/util/di/context.gpt.hh"
#include "cs/util/time.hh"
#include "cs/util/uuid.hh"

namespace {  // use_usings
}  // namespace

namespace {  // impl
// djb2
std::string HashPassword(std::string password,
                         std::string salt) {
  unsigned long hash = 5381;
  auto str = salt + password + salt;
  for (char c : str) {
    hash = ((hash << 5) + hash) + c;  // hash * 33 + c
  }

  std::stringstream ss;
  ss << std::hex << hash;
  return ss.str();
}

}  // namespace

namespace cs::apps::trycopilotai::api {

using ::cs::apps::trycopilotai::protos::CreateUserRequest;
using ::cs::apps::trycopilotai::protos::CreateUserResponse;

IMPLEMENT_RPC(CreateUserRPC, CreateUserRequest,
              CreateUserResponse) {
  if (request.email.empty()) {
    return TRACE(
        ::cs::InvalidArgument("Email must not be empty."));
  }
  if (request.password.empty()) {
    return TRACE(::cs::InvalidArgument(
        "Password must not be empty."));
  }
  if (request.confirm_password.empty()) {
    return TRACE(::cs::InvalidArgument(
        "Password confirmation must not be empty."));
  }
  if (request.password != request.confirm_password) {
    return TRACE(
        ::cs::InvalidArgument("Passwords do not match."));
  }

  SET_OR_RET(auto* ctx, GetContext());
  auto db =
      ctx->Get<::cs::net::proto::db::IDatabaseClient>();
  SET_OR_RET(bool user_exists,
             ::cs::net::proto::db::QueryView<
                 ::cs::apps::trycopilotai::protos::User>(
                 "users", db)
                 .where(::cs::net::proto::db::EQUALS(
                     &::cs::apps::trycopilotai::protos::
                         User::email,
                     request.email))
                 .any());
  if (user_exists) {
    return TRACE(::cs::Error(
        "User with this email already exists."));
  }

  ::cs::apps::trycopilotai::protos::User user;
  user.email = request.email;
  user.password.salt =
      ::cs::util::random::uuid::generate_uuid_v4();
  user.password.hash =
      HashPassword(request.password, user.password.salt);
  user.uuid = ::cs::util::random::uuid::generate_uuid_v4();
  user.admin = false;

  OK_OR_RET(::cs::net::proto::db::Upsert(
      "users", user,
      *ctx->Get<::cs::net::proto::db::IDatabaseClient>()));

  ::cs::apps::trycopilotai::protos::CreateUserResponse
      response;
  response.email = user.email;
  response.user_uuid = user.uuid;

  return response;
}

}  // namespace cs::apps::trycopilotai::api

namespace cs::apps::trycopilotai::rpc {

using ::cs::apps::trycopilotai::protos::CreateUserRequest;
using ::cs::apps::trycopilotai::protos::CreateUserResponse;

::cs::ResultOr<CreateUserResponse> CreateUserRPC::Process(
    const CreateUserRequest& request) {
  SET_OR_RET(auto* ctx, GetContext());
  return ::cs::apps::trycopilotai::api::CreateUserRPC(*ctx)
      .Process(request);
}

}  // namespace cs::apps::trycopilotai::rpc
v0 (commit) © 2025 @p13i.io | Load balancer proxied to: cs-code-viewer-1:8080 in 2ms.