Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions app/server/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,14 @@ ServerConfig load_server_config(const std::filesystem::path & path) {
throw std::runtime_error("server threads must be positive");
}

const auto * apikeys = root.find("apikeys");
if (apikeys && !apikeys->is_array()) {
throw std::runtime_error("server config, apikeys must be an array of strings");
}
for (const auto & item : apikeys->as_array()) {
config.apikeys.insert(std::make_pair(item.as_string(),true));
}

const auto * models = root.find("models");
if (models == nullptr || !models->is_array()) {
throw std::runtime_error("server config requires a models array");
Expand Down
2 changes: 2 additions & 0 deletions app/server/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <string>
#include <unordered_map>
#include <vector>
#include <map>

#include "engine/framework/core/backend.h"

Expand Down Expand Up @@ -99,6 +100,7 @@ struct ServerConfig {
// model preset resolves to <voice_dir>/<name>.wav as the cloning reference.
std::optional<std::filesystem::path> voice_dir;
std::vector<ServerModelConfig> models;
std::map<std::string,bool> apikeys;
};

engine::core::BackendType parse_server_backend(const std::string & value);
Expand Down
27 changes: 27 additions & 0 deletions app/server/runtime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -977,11 +977,36 @@ ServerState::~ServerState() {
}
}

class AuthError : public std::runtime_error {
public:
using std::runtime_error::runtime_error;
};

HttpResponse ServerState::handle(const HttpRequest & request) {
HttpResponse response;
const std::string allowed_origin = get_allowed_origin(request);
try {
log_request_body_if_enabled(config_, request);

if (!config_.apikeys.empty()) {
if (const auto it = request.headers.find("authorization"); it != request.headers.end()) {
std::string auth,authtype,apikey;

auth = it->second;
for (int i = 0; i<auth.size() && std::isalpha(auth[i]); i++) {
authtype += auth[i];
}

apikey = auth.substr(authtype.size() + 1);

if (config_.apikeys.find(apikey) == config_.apikeys.end()) {
throw AuthError("authorization failed");
}
} else {
throw AuthError("authorization failed");
}
}

if (request.method == "OPTIONS" && (!allowed_origin.empty() || config_.ui_enabled)) {
response.status = 204;
response.content_type = "text/plain";
Expand Down Expand Up @@ -1104,6 +1129,8 @@ HttpResponse ServerState::handle(const HttpRequest & request) {
// sent. (Streaming requests acquire the lock inside the stream body, after
// headers are sent, so there it becomes a stream error event instead.)
response = error_response(503, ex.what(), "server_busy");
} catch (const AuthError & ex) {
response = error_response(401, ex.what(), "auth_error");
}
if (!allowed_origin.empty()) {
response.headers["Access-Control-Allow-Origin"] = allowed_origin;
Expand Down
Loading