瀏覽代碼

feat: get and find users

Bryan Lee 1 年之前
父節點
當前提交
395ef80a3c

+ 2 - 1
authentication/.gitignore

@@ -1 +1,2 @@
-target/
+target/
+.env

+ 263 - 0
authentication/Cargo.lock

@@ -233,6 +233,21 @@ dependencies = [
  "alloc-no-stdlib",
 ]
 
+[[package]]
+name = "android-tzdata"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
+
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
 [[package]]
 name = "anstream"
 version = "0.6.11"
@@ -286,7 +301,12 @@ name = "authentication"
 version = "0.1.0"
 dependencies = [
  "actix-web",
+ "diesel",
+ "dotenvy",
  "env_logger",
+ "is_empty",
+ "serde",
+ "uuid",
 ]
 
 [[package]]
@@ -358,6 +378,18 @@ dependencies = [
  "alloc-stdlib",
 ]
 
+[[package]]
+name = "bumpalo"
+version = "3.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
+
+[[package]]
+name = "byteorder"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
+
 [[package]]
 name = "bytes"
 version = "1.5.0"
@@ -389,6 +421,18 @@ version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
+[[package]]
+name = "chrono"
+version = "0.4.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f13690e35a5e4ace198e7beea2895d29f3a9cc55015fcebe6336bd2010af9eb"
+dependencies = [
+ "android-tzdata",
+ "iana-time-zone",
+ "num-traits",
+ "windows-targets 0.52.0",
+]
+
 [[package]]
 name = "colorchoice"
 version = "1.0.0"
@@ -412,6 +456,12 @@ dependencies = [
  "version_check",
 ]
 
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
+
 [[package]]
 name = "cpufeatures"
 version = "0.2.12"
@@ -462,6 +512,43 @@ dependencies = [
  "syn 1.0.109",
 ]
 
+[[package]]
+name = "diesel"
+version = "2.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62c6fcf842f17f8c78ecf7c81d75c5ce84436b41ee07e03f490fbb5f5a8731d8"
+dependencies = [
+ "bitflags 2.4.2",
+ "byteorder",
+ "chrono",
+ "diesel_derives",
+ "itoa",
+ "pq-sys",
+ "r2d2",
+ "uuid",
+]
+
+[[package]]
+name = "diesel_derives"
+version = "2.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef8337737574f55a468005a83499da720f20c65586241ffea339db9ecdfd2b44"
+dependencies = [
+ "diesel_table_macro_syntax",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.48",
+]
+
+[[package]]
+name = "diesel_table_macro_syntax"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc5557efc453706fed5e4fa85006fe9817c224c3f480a34c7e5959fd700921c5"
+dependencies = [
+ "syn 2.0.48",
+]
+
 [[package]]
 name = "digest"
 version = "0.10.7"
@@ -472,6 +559,12 @@ dependencies = [
  "crypto-common",
 ]
 
+[[package]]
+name = "dotenvy"
+version = "0.15.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
+
 [[package]]
 name = "encoding_rs"
 version = "0.8.33"
@@ -646,6 +739,29 @@ version = "2.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
 
+[[package]]
+name = "iana-time-zone"
+version = "0.1.60"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
+dependencies = [
+ "android_system_properties",
+ "core-foundation-sys",
+ "iana-time-zone-haiku",
+ "js-sys",
+ "wasm-bindgen",
+ "windows-core",
+]
+
+[[package]]
+name = "iana-time-zone-haiku"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
+dependencies = [
+ "cc",
+]
+
 [[package]]
 name = "idna"
 version = "0.5.0"
@@ -666,6 +782,26 @@ dependencies = [
  "hashbrown",
 ]
 
+[[package]]
+name = "is_empty"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0a936154b7f653894729875d5593b3251d90619c90994581dceec44334df4021"
+dependencies = [
+ "is_empty_derive",
+]
+
+[[package]]
+name = "is_empty_derive"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8903009eceffe882e7cb6adadd29001f4f0e46f67616247f40841a5604f1610"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
 [[package]]
 name = "itoa"
 version = "1.0.10"
@@ -681,6 +817,15 @@ dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "js-sys"
+version = "0.3.67"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1"
+dependencies = [
+ "wasm-bindgen",
+]
+
 [[package]]
 name = "language-tags"
 version = "0.3.2"
@@ -765,6 +910,15 @@ version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
 
+[[package]]
+name = "num-traits"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
+dependencies = [
+ "autocfg",
+]
+
 [[package]]
 name = "object"
 version = "0.32.2"
@@ -845,6 +999,15 @@ version = "0.2.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
 
+[[package]]
+name = "pq-sys"
+version = "0.4.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "31c0052426df997c0cbd30789eb44ca097e3541717a7b8fa36b1c464ee7edebd"
+dependencies = [
+ "vcpkg",
+]
+
 [[package]]
 name = "proc-macro2"
 version = "1.0.78"
@@ -863,6 +1026,17 @@ dependencies = [
  "proc-macro2",
 ]
 
+[[package]]
+name = "r2d2"
+version = "0.8.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51de85fb3fb6524929c8a2eb85e6b6d363de4e8c48f9e2c2eac4944abc181c93"
+dependencies = [
+ "log",
+ "parking_lot",
+ "scheduled-thread-pool",
+]
+
 [[package]]
 name = "rand"
 version = "0.8.5"
@@ -952,6 +1126,15 @@ version = "1.0.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
 
+[[package]]
+name = "scheduled-thread-pool"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3cbc66816425a074528352f5789333ecff06ca41b36b0b0efdfbb29edc391a19"
+dependencies = [
+ "parking_lot",
+]
+
 [[package]]
 name = "scopeguard"
 version = "1.2.0"
@@ -1215,6 +1398,23 @@ version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
 
+[[package]]
+name = "uuid"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a"
+dependencies = [
+ "getrandom",
+ "rand",
+ "serde",
+]
+
+[[package]]
+name = "vcpkg"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
+
 [[package]]
 name = "version_check"
 version = "0.9.4"
@@ -1227,6 +1427,69 @@ version = "0.11.0+wasi-snapshot-preview1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
 
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.90"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.90"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.48",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.90"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.90"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.48",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.90"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b"
+
+[[package]]
+name = "windows-core"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
+dependencies = [
+ "windows-targets 0.52.0",
+]
+
 [[package]]
 name = "windows-sys"
 version = "0.48.0"

+ 10 - 0
authentication/Cargo.toml

@@ -7,4 +7,14 @@ edition = "2021"
 
 [dependencies]
 actix-web = "4.5.1"
+diesel = { version = "2.1.4", features = [
+  "postgres",
+  "r2d2",
+  "chrono",
+  "uuid",
+] }
+dotenvy = "0.15.7"
 env_logger = "0.11.1"
+is_empty = "0.2.0"
+serde = { version = "1.0.196", features = ["derive"] }
+uuid = { version = "1.7.0", features = ["v4", "fast-rng", "serde"] }

+ 14 - 0
authentication/README.md

@@ -5,3 +5,17 @@ An NGINX proxy provides TLS by forwarding ports defined below:
 | Internal service port | External port with TLS | Protocol | Description             |
 | --------------------- | ---------------------- | -------- | ----------------------- |
 | `18000`               | `8000`                 | HTTP     | The authentication API. |
+
+## Database setup
+
+We use `diesel-cli` for database migrations.
+
+To setup `diesel`, run:
+
+```bash
+diesel setup --database-url='postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@localhost:15432/${POSTGRES_DB}'
+```
+
+Take the variables from the project `compose.yaml` file.
+
+Refer to [`diesel-cli`](https://crates.io/crates/diesel_cli) for usage documentation.

+ 9 - 0
authentication/diesel.toml

@@ -0,0 +1,9 @@
+# For documentation on how to configure this file,
+# see https://diesel.rs/guides/configuring-diesel-cli
+
+[print_schema]
+file = "src/schema.rs"
+custom_type_derives = ["diesel::query_builder::QueryId"]
+
+[migrations_directory]
+dir = "migrations"

+ 0 - 0
authentication/migrations/.gitkeep


+ 6 - 0
authentication/migrations/00000000000000_diesel_initial_setup/down.sql

@@ -0,0 +1,6 @@
+-- This file was automatically created by Diesel to setup helper functions
+-- and other internal bookkeeping. This file is safe to edit, any future
+-- changes will be added to existing projects as new migrations.
+
+DROP FUNCTION IF EXISTS diesel_manage_updated_at(_tbl regclass);
+DROP FUNCTION IF EXISTS diesel_set_updated_at();

+ 36 - 0
authentication/migrations/00000000000000_diesel_initial_setup/up.sql

@@ -0,0 +1,36 @@
+-- This file was automatically created by Diesel to setup helper functions
+-- and other internal bookkeeping. This file is safe to edit, any future
+-- changes will be added to existing projects as new migrations.
+
+
+
+
+-- Sets up a trigger for the given table to automatically set a column called
+-- `updated_at` whenever the row is modified (unless `updated_at` was included
+-- in the modified columns)
+--
+-- # Example
+--
+-- ```sql
+-- CREATE TABLE users (id SERIAL PRIMARY KEY, updated_at TIMESTAMP NOT NULL DEFAULT NOW());
+--
+-- SELECT diesel_manage_updated_at('users');
+-- ```
+CREATE OR REPLACE FUNCTION diesel_manage_updated_at(_tbl regclass) RETURNS VOID AS $$
+BEGIN
+    EXECUTE format('CREATE TRIGGER set_updated_at BEFORE UPDATE ON %s
+                    FOR EACH ROW EXECUTE PROCEDURE diesel_set_updated_at()', _tbl);
+END;
+$$ LANGUAGE plpgsql;
+
+CREATE OR REPLACE FUNCTION diesel_set_updated_at() RETURNS trigger AS $$
+BEGIN
+    IF (
+        NEW IS DISTINCT FROM OLD AND
+        NEW.updated_at IS NOT DISTINCT FROM OLD.updated_at
+    ) THEN
+        NEW.updated_at := current_timestamp;
+    END IF;
+    RETURN NEW;
+END;
+$$ LANGUAGE plpgsql;

+ 2 - 0
authentication/migrations/2024-02-06-171601_create_user_table/down.sql

@@ -0,0 +1,2 @@
+-- This file should undo anything in `up.sql`
+drop table "user";

+ 9 - 0
authentication/migrations/2024-02-06-171601_create_user_table/up.sql

@@ -0,0 +1,9 @@
+create table "user" (
+  id uuid primary key default gen_random_uuid() not null,
+  email text unique,
+  email_verified boolean not null default false,
+  locale text,
+  oauth2_id text,
+  oauth2_name text,
+  oauth2_picture_url text
+);

+ 1 - 0
authentication/sample.env

@@ -0,0 +1 @@
+DATABASE_URL=

+ 9 - 0
authentication/src/lib.rs

@@ -0,0 +1,9 @@
+use diesel::{r2d2, PgConnection};
+
+pub mod schema;
+pub mod user;
+
+/// Short-hand for the database pool type to use throughout the app.
+pub type DbPool = r2d2::Pool<r2d2::ConnectionManager<PgConnection>>;
+
+pub type DbError = Box<dyn std::error::Error + Send + Sync>;

+ 24 - 8
authentication/src/main.rs

@@ -1,11 +1,8 @@
-use actix_web::{get, App, HttpResponse, HttpServer, Responder};
+use actix_web::{get, middleware, web, App, HttpServer, Responder};
+use authentication::{user, DbPool};
+use diesel::{r2d2, PgConnection};
 use std::env;
 
-#[get("/healthcheck")]
-async fn healthcheck() -> impl Responder {
-    HttpResponse::Ok()
-}
-
 #[get("/")]
 async fn hello() -> impl Responder {
     "MultiplayerBase Authentication Server"
@@ -16,15 +13,34 @@ const PORT: u16 = 8000;
 
 #[actix_web::main]
 async fn main() -> std::io::Result<()> {
+    dotenvy::dotenv().ok();
     env::set_var("RUST_LOG", "actix_web=debug,actix_server=info");
     env_logger::init();
 
-    HttpServer::new(|| {
+    let pool = initialize_db_pool();
+
+    HttpServer::new(move || {
         App::new()
-            .service(healthcheck)
+            .app_data(web::Data::new(pool.clone()))
+            .wrap(middleware::Logger::default())
+            .wrap(middleware::NormalizePath::new(
+                middleware::TrailingSlash::Always,
+            ))
             .service(hello)
+            .service(web::scope("/user").configure(user::config_service))
     })
     .bind((HOST, PORT))?
     .run()
     .await
 }
+
+/// Initialize a database connection pool based on the `DATABASE_URL` environment variable.
+///
+/// See more: <https://docs.rs/diesel/latest/diesel/r2d2/index.html>.
+fn initialize_db_pool() -> DbPool {
+    let conn_spec = std::env::var("DATABASE_URL").expect("DATABASE_URL should be set");
+    let manager = r2d2::ConnectionManager::<PgConnection>::new(conn_spec);
+    r2d2::Pool::builder()
+        .build(manager)
+        .expect("DATABASE_URL should be a valid Postgres connection string")
+}

+ 13 - 0
authentication/src/schema.rs

@@ -0,0 +1,13 @@
+// @generated automatically by Diesel CLI.
+
+diesel::table! {
+    user (id) {
+        id -> Uuid,
+        email -> Nullable<Text>,
+        email_verified -> Bool,
+        locale -> Nullable<Text>,
+        oauth2_id -> Nullable<Text>,
+        oauth2_name -> Nullable<Text>,
+        oauth2_picture_url -> Nullable<Text>,
+    }
+}

+ 85 - 0
authentication/src/user.rs

@@ -0,0 +1,85 @@
+use crate::{DbError, DbPool};
+use actix_web::{error, get, web, HttpResponse, Responder};
+use diesel::prelude::*;
+use is_empty::IsEmpty;
+use serde::{Deserialize, Serialize};
+use uuid::Uuid;
+
+#[derive(Serialize, Deserialize, Debug, Clone, Queryable, Selectable, Insertable, AsChangeset)]
+#[diesel(table_name = crate::schema::user)]
+#[diesel(check_for_backend(diesel::pg::Pg))]
+pub struct User {
+    #[serde(default)]
+    pub id: Uuid,
+    pub email: Option<String>,
+    pub email_verified: bool,
+    pub locale: Option<String>,
+    pub oauth2_id: Option<String>,
+    pub oauth2_name: Option<String>,
+    pub oauth2_picture_url: Option<String>,
+}
+
+pub fn config_service(cfg: &mut web::ServiceConfig) {
+    cfg.service(get_user_by_id).service(find_user);
+}
+
+#[get("/{user_id}")]
+async fn get_user_by_id(
+    pool: web::Data<DbPool>,
+    path: web::Path<Uuid>,
+) -> actix_web::Result<impl Responder> {
+    let user_id = path.into_inner();
+
+    // Use `web::block` to offload blocking Diesel queries without blocking server thread.
+    let user = web::block(move || {
+        // Obtaining a connection from the pool is also potentially blocking.
+        let mut conn = pool.get()?;
+
+        use crate::schema::user::dsl::*;
+        let mut query = user.into_boxed();
+        query = query.filter(id.eq(user_id));
+        Ok(query.first::<User>(&mut conn).optional()?)
+    })
+    .await?
+    .map_err(error::ErrorInternalServerError::<DbError>)?;
+
+    Ok(match user {
+        Some(user) => HttpResponse::Ok().json(user),
+        None => HttpResponse::NotFound().body(format!("No user found with id {user_id}")),
+    })
+}
+
+#[derive(Deserialize, IsEmpty)]
+struct FindUserQueryParams {
+    email: Option<String>,
+}
+
+#[get("/")]
+async fn find_user(
+    pool: web::Data<DbPool>,
+    params: web::Query<FindUserQueryParams>,
+) -> actix_web::Result<impl Responder> {
+    if params.is_empty() {
+        return Ok(HttpResponse::BadRequest().body("At least one query parameter is required"));
+    }
+
+    // Use `web::block` to offload blocking Diesel queries without blocking server thread.
+    let user = web::block(move || {
+        // Obtaining a connection from the pool is also potentially blocking.
+        let mut conn = pool.get()?;
+
+        use crate::schema::user::dsl::*;
+        let mut query = user.into_boxed();
+        if let Some(_email) = &params.email {
+            query = query.filter(email.eq(_email));
+        }
+        Ok(query.first::<User>(&mut conn).optional()?)
+    })
+    .await?
+    .map_err(error::ErrorInternalServerError::<DbError>)?;
+
+    Ok(match user {
+        Some(user) => HttpResponse::Ok().json(user),
+        None => HttpResponse::NotFound().body(format!("No user found")),
+    })
+}

+ 4 - 2
compose.yaml

@@ -29,9 +29,11 @@ services:
     volumes:
       - db-data:/var/lib/postgresql/data
     environment:
+      POSTGRES_USER: postgres
       POSTGRES_PASSWORD_FILE: /run/secrets/db-password
-    expose:
-      - 5432
+      POSTGRES_DB: postgres
+    ports:
+      - 15432:5432
     healthcheck:
       test: ["CMD", "pg_isready"]
       interval: 10s

+ 1 - 1
project/game/game_client.gd

@@ -6,7 +6,7 @@ const _DEFAULT_SERVER_HOST := "127.0.0.1"
 var env_server_host := OS.get_environment("SERVER_HOST")
 var server_host := env_server_host if env_server_host else _DEFAULT_SERVER_HOST
 
-const _DEFAULT_SERVER_PORT := 19000
+const _DEFAULT_SERVER_PORT := 9000
 var env_server_port := OS.get_environment("SERVER_PORT")
 var server_port := int(env_server_port) if env_server_port else _DEFAULT_SERVER_PORT
 

+ 1 - 1
project/game/game_server.gd

@@ -1,7 +1,7 @@
 extends Node
 class_name GameServer
 
-const _DEFAULT_PORT := 19000
+const _DEFAULT_PORT := 9000
 var env_port := OS.get_environment("PORT")
 var port := int(env_port) if env_port else _DEFAULT_PORT