123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624 |
- alias uint8 = ubyte;
- //import vibe.vibe;
- // https://github.com/vibe-d/vibe.d/blob/master/source/vibe/vibe.d
- import vibe.core.core;
- import vibe.http.router;
- import vibe.http.server;
- import vibe.http.fileserver;
- import vibe.http.websockets;
- import vibe.core.log;
- import ws_bert_login : ws_bert_handle, login_test; // login - logged - logout -- via ws with bert ++ memcached + postgresql for sessions
- import std.string;
- import std.array;
- import std.algorithm;
- import std.variant : Variant;
- import std.datetime : SysTime, Clock;
- //import core.sync.mutex : Mutex;
- import std.concurrency : spawn;
- import vibe.core.concurrency;
- import vibe.core.core : Task, sleep;
- import core.time : Duration, dur;
- /+
- Tid user_state_pid; // with dlang message passing like in Erlang -- err here - do not tick every 30 sec
- struct UserState2{
- string client_id;
- SysTime last_online_at;
- }
- alias UserStateMap2 = UserState2[string];
- UserStateMap2 user_states;
- struct Msg1{
- uint8 command;
- }
- /*
- struct Msg2{
- Tid sender;
- string client_id;
- }
- */
- struct Msg21{
- uint8 command;
- string client_id;
- }
- struct Msg3{
- Tid sender;
- uint8 command;
- string client_id;
- }
- +/
- /*
- struct UserState{ // with mutex
- string client_id;
- SysTime last_online_at;
-
- this(string client_id, SysTime last_online_at) pure nothrow @safe{
- this.client_id = client_id;
- this.last_online_at = last_online_at;
- }
- }
- alias UserStateMap = UserState[string];
- class UserStateManager{ // with mutex
- private UserStateMap states;
- private Object lockObj;
-
- this(){
- lockObj = new Object();
- }
-
- bool contains(string client_id){
- synchronized(lockObj){
- return (client_id in states) !is null;
- }
- }
-
- void addOrUpdate(string client_id){
- synchronized(lockObj){
- auto now = Clock.currTime();
- states.remove(client_id);
- states[client_id] = UserState(client_id, now);
- }
- }
-
- void cleanup(){
- synchronized(lockObj){
- auto now = Clock.currTime();
- auto toRemove = states.byKey
- .filter!(k => (now - states[k].last_online_at).total!"seconds" > 30)
- .array;
-
- foreach(client_id; toRemove){
- states.remove(client_id);
- }
- }
- }
-
- size_t length() const{
- synchronized(lockObj){
- return states.length;
- }
- }
- }
- __gshared UserStateManager userStateManager;
- */
- import memcached4d;
- import std.conv : to;
- import tr;
- import mustache;
- alias MustacheEngine!(string) Mustache;
- import vibe.db.postgresql;
- import vibe.data.bson;
- //import vibe.data.json;
- // https://github.com/vibe-d/vibe.d/blob/master/source/vibe/vibe.d
- PostgresClient[] clients;
- import settings_toml; // get settings from settings.toml, settings keys, settings validation etc
- /* test unproper arguments order */
- /* do not */
- /*
- alias test_key1 = int;
- alias test_key2 = int;
- string test_args_types_mismash(test_key1 x, test_key2 y){
- return ( to!string(x) ~ " + " ~ to!string(y) ~ " = " ~ to!string( x + y ) );
- }
- */
- /* do not */
- /*
- import std.typecons : Typedef;
- alias test_key5 = Typedef!int;
- alias test_key6 = Typedef!int;
- string test_args_types_mismash(test_key5 x, test_key6 y){
- return ( to!string(x) ~ " + " ~ to!string(y) ~ " = " ~ to!string( x + y ) );
- }
- */
- /* do like */
- import std.typecons : Typedef;
- alias test_key5 = Typedef!(int, int.init, "key5");
- alias test_key6 = Typedef!(int, int.init, "key6");
- string test_args_types_mismash(test_key5 x, test_key6 y){
- return ( to!string(x) ~ " + " ~ to!string(y) ~ " = " ~ to!string( x + y ) );
- }
- /* or - do like */
- struct test_key3 { int v; }
- struct test_key4 { int v; }
- string test_args_types_mismash2(test_key3 x, test_key4 y){
- return ( to!string(x.v) ~ " + " ~ to!string(y.v) ~ " = " ~ to!string( x.v + y.v ) );
- }
- /*
- void startCleanupTask(){ // with mutex
- while(true){
- writeln("startCleanupTask();");
- userStateManager.cleanup();
- //sleep(dur!"hours"(1)); // every 1 hour = 3600 sec
- sleep(dur!"seconds"(30)); // every 30 sec
- }
- }
- */
- /+
- void startCleanupTask2(){ // with message passing (actors model like in Erlang); worker -- err here - do not tick every 30 sec
- writeln("startCleanupTask2();");
- while(true){
- /*
- auto msg = receiveOnly!(Tid, uint8, string)();
- writeln("int: ", msg[1]); // got int
- writeln("string: ", msg[2]); // got string
- msg[0].send(thisTid); // send message back
- */
-
- receive(
- (Msg1 msg){
- if(msg.command == 0){ // 0 for delete inactive clients
- writeln("221 ", user_states.keys); // show all keys
- auto now = Clock.currTime();
- auto toRemove = user_states.byKey
- .filter!(k => (now - user_states[k].last_online_at).total!"seconds" > 30) // clean every 30 seconds
- .array;
-
- foreach(client_id; toRemove){
- user_states.remove(client_id);
- }
- }
- return true;
- },
-
- /*
- (Msg2 msg){
- msg.sender
- msg.client_id
- },
- */
-
- (Msg21 msg){
- if(msg.command == 2){ // 2 for add_or_upd client
- //auto now = Clock.currTime();
- user_states[msg.client_id] = UserState2( client_id : msg.client_id, last_online_at : Clock.currTime() );
- //writeln("user_states.length = ", user_states.length);
- //writeln(user_states.keys); // show all keys
- //writeln(user_states.values); // show all values
- }else if(msg.command == 3){ // 3 for delete client
- user_states.remove(msg.client_id);
- }
- return true;
- },
-
- (Msg3 msg){
- if(msg.command == 1){ // 1 for check is new client
- //msg.sender.send( (msg.client_id in user_states) !is null ); // is client exists
- msg.sender.send( (msg.client_id in user_states) is null ); // is new client
- }
- //}else if(msg.command == 4){ // 4 for get length
- // msg.sender.send(user_states.length);
- //}
- return true;
- },
-
- (Variant v){
- writeln("got unexpected Variant v: ", v);
- return true;
- }
- );
- }
- }
- void startCleanupTask21(){ // clean daemon
- while(true){
- writeln("do clean!");
- writeln("273", user_states.keys); // show all keys
- user_state_pid.send(Msg1(0)); // 0 for delete inactive clients
- writeln("275", user_states.keys); // show all keys
- sleep(dur!"seconds"(30)); // clean every 30 seconds
- }
- }
- +/
- void main(){
- read_settings_toml(); // read settings, settings validation
-
- uint conn_num = cast(uint) toml_s[s_toml_db][s_toml_db_conn_num].integer();
- uint8 i = cast(uint8) conn_num;
- // https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING
- // https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-PARAMKEYWORDS
- //client = new PostgresClient("host=localhost port=5432 dbname=mydb user=username password=pass connect_timeout=5", 100);
- while(i > 0){
- //writeln("i = ", i);
- clients ~= new PostgresClient( "host=" ~ toml_s[s_toml_db][s_toml_db_host].str() ~
- " port=" ~ toml_s[s_toml_db][s_toml_db_port].str() ~
- " dbname=" ~ toml_s[s_toml_db][s_toml_db_name].str() ~
- " user=" ~ toml_s[s_toml_db][s_toml_db_user].str() ~
- " password=" ~ toml_s[s_toml_db][s_toml_db_pass].str() ~
- " connect_timeout=" ~ toml_s[s_toml_db][s_toml_db_conn_timeout].str(),
- conn_num,
- (scope Connection conn){
- conn.prepareEx("get_city_by_id", "SELECT id, name, population FROM test WHERE id = $1");
- } );
-
- i--;
- }
-
-
-
- /*
- userStateManager = new UserStateManager(); // with mutex
- userStateManager.addOrUpdate("123");
- */
-
-
-
- auto settings = new HTTPServerSettings;
- //settings.port = 8080;
- //settings.bindAddresses = ["::1", "127.0.0.1"];
- settings.port = cast(ushort)toml_s[s_toml_http][s_toml_http_port].integer();
- settings.bindAddresses = [ toml_s[s_toml_http][s_toml_http_host].str().idup ];
-
-
- //auto fsettings = new HTTPFileServerSettings;
- //fsettings.serverPathPrefix = "/static";
-
- auto router = new URLRouter;
- //router.get("/", &index);
- ////router.get("static/*", serverStaticFiles("public/", fsettings) );
-
- //router.get("/", staticTemplate!"index.html");
- router.get("/", serveStaticFile("public/index.html") ); // static html + ws echo example
- router.get("/ws", handleWebSockets(&ws_handle) ); // static html + ws echo example
- router.get("/test", &test); // Mustache template + memcached + postgresql pool example
- router.get("/ws_login_test", handleWebSockets(&ws_bert_handle) ); // ws handler begins from "ws_" and next same http page path // login - logged - logout -- via ws with bert
- router.get("/login_test", &login_test); // login - logged - logout -- via ws with bert
- router.get("*", serveStaticFiles("public/"));
-
- //auto listener = listenHTTP(settings, &hello);
- auto listener = listenHTTP(settings, router);
- scope (exit){
- listener.stopListening();
- }
-
-
-
- //writeln("userStateManager.states is null? ", userStateManager.states is null);
- //sleep(dur!"seconds"(1));
-
- //spawn(&startCleanupTask); // clean inactive user state -- with mutex
-
- /*
- user_state_pid = spawn(&startCleanupTask2); // worker - clean inactive user state -- with message passing (actors model like in Erlang) -- err here - do not tick every 30 sec
- spawn(&startCleanupTask21); // daemon for worker
- //auto clean_daemon_pid = spawn(&startCleanupTask21, thisTid);
- sleep(dur!"seconds"(5));
- */
-
- logInfo("Please open http://127.0.0.1:8080/ in your browser.");
- runApplication();
- }
- void ws_handle(scope WebSocket sock){
- // simple echo server + :)
-
- //writeln("sock = ", sock); // vibe.http.websockets.WebSocket
- //writeln("sock.request = ", sock.request); // GET /ws?client_id=YaHoAnZo3JPYOwX7yn35 HTTP/1.1
- //writeln("sock.request.requestPath = ", sock.request.requestPath); // /ws
- //writeln("sock.request.queryString = ", sock.request.queryString); // client_id=YaHoAnZo3JPYOwX7yn35
- //writeln("sock.request.query = ", sock.request.query); // ["client_id": "YaHoAnZo3JPYOwX7yn35"]
-
- string client_id = "";
- if("client_id" in sock.request.query){
- client_id = sock.request.query["client_id"];
- //writeln("found client_id = ", client_id);
- }else{
- //writeln("client_id not found");
- throw new StringException("client_id not found");
- }
-
- /*
- bool is_new_client = true;
- if(userStateManager.contains(client_id)){ // used with mutex
- is_new_client = false;
- writeln("Reconnection from existing client: ", client_id);
- }else{
- writeln("New client connected: ", client_id);
- }
- userStateManager.addOrUpdate(client_id);
- */
-
- /*
- user_state_pid.send(Msg3(thisTid, 1, client_id)); // 1 for check is client exists -- err here - do not tick every 30 sec
- //enforce(receiveOnly!Tid() == tid);
- //auto response = receiveOnly!(Tid, bool)(); // response[0] = Tid of worker; response[1] = bool result
- bool is_new_client = receiveOnly!bool();
- if(is_new_client){
- writeln("New client connected: ", client_id);
- }else{
- writeln("Reconnection from existing client: ", client_id);
- }
- user_state_pid.send(Msg21(2, client_id));
- */
-
-
- /*
- try{
- while(sock.connected){
- auto msg = sock.receiveText();
- sock.send(msg ~ " :)");
- }
- }catch(Exception ex){
- writeln("disconnected client_id = ", client_id);
- writeln("Error: ", ex.msg); // Error: Connection closed while reading message.
- }
- */
- while(sock.waitForData()){
- auto msg = sock.receiveText();
- sock.send(msg ~ " :)");
- }
- writeln("after disconnect"); // now shows
- }
- /*
- void index(HTTPServerRequest req, HTTPServerResponse res){
- res.writeBody("Hello, World!");
- }
- */
- /*
- void hello(HTTPServerRequest req, HTTPServerResponse res){
- res.writeBody("Hello, World!");
- }
- */
- /*
- void test_pg_conn_driver(){ // https://github.com/denizzzka/vibe.d.db.postgresql/blob/master/example/example.d#L13
- client.pickConnection( (scope conn){
- immutable result = conn.exec(
- "SELECT 123 as first_num, 567 as second_num, 'abc'::text as third_text " ~
- "UNION ALL " ~
- "SELECT 890, 233, 'fgh'::text as third_text",
- ValueFormat.BINARY
- );
-
- assert(result[0]["second_num"].as!PGinteger == 567);
- assert(result[1]["third_text"].as!PGtext == "fgh");
-
- foreach (val; rangify(result[0])){
- writeln("Found entry: ", val.as!Bson.toJson);
- }
- } );
- }
- */
- string get_all_cities(){
- return "SELECT id, name, population FROM test ORDER BY id";
- }
- void test_pg_conn_driver_queries(){
- /*
- client.pickConnection( (scope conn){
- QueryParams params; // https://github.com/denizzzka/dpq2/blob/master/src/dpq2/args.d#L15
- params.preparedStatementName = "get_city_by_id";
- params.argsVariadic(3); // https://github.com/denizzzka/dpq2/blob/master/example/example.d#L42 // https://github.com/denizzzka/dpq2/blob/master/src/dpq2/query.d#L336 // https://github.com/denizzzka/vibe.d.db.postgresql/blob/master/source/vibe/db/postgresql/package.d#L423
- conn.prepareEx(params.preparedStatementName, "SELECT id, name, population FROM test WHERE id = $1"); // get_city_by_id
- auto result1 = conn.execPrepared(params);
-
- writeln("id: ", result1[0]["id"].as!PGinteger);
- writeln("name: ", result1[0]["name"].as!PGtext);
- writeln("population: ", result1[0]["population"].as!PGinteger);
-
- //conn.prepareEx("q1", "UPDATE test SET name = $1, population = $2 WHERE id = $3"); // update_city_by_id
- //immutable result1 = conn.execPrepared("", ValueFormat.BINARY);
-
- destroy(conn);
- } );
- */
-
- //auto conn = clients[0].lockConnection(); // todo think is this correct to take index 0 here
- clients[0].pickConnection( (scope conn){ // todo think is this correct to take index 0 here
- QueryParams params; // https://github.com/denizzzka/dpq2/blob/master/src/dpq2/args.d#L15
- params.preparedStatementName = "get_city_by_id";
- params.argsVariadic(3); // https://github.com/denizzzka/dpq2/blob/master/example/example.d#L42 // https://github.com/denizzzka/dpq2/blob/master/src/dpq2/query.d#L336 // https://github.com/denizzzka/vibe.d.db.postgresql/blob/master/source/vibe/db/postgresql/package.d#L423
- //conn.prepareEx(params.preparedStatementName, "SELECT id, name, population FROM test WHERE id = $1"); // get_city_by_id
- auto result1 = conn.execPrepared(params);
-
- writeln("id: ", result1[0]["id"].as!PGinteger);
- writeln("name: ", result1[0]["name"].as!PGtext);
- writeln("population: ", result1[0]["population"].as!PGinteger);
-
- //conn.prepareEx("q1", "UPDATE test SET name = $1, population = $2 WHERE id = $3"); // update_city_by_id
- //immutable result1 = conn.execPrepared("", ValueFormat.BINARY);
-
- destroy(conn);
- } );
- }
- /*
- "SELECT id, name, population FROM test ORDER BY id"
- "UPDATE test SET name = $1, population = $2 WHERE id = $3", [City_Name, City_Pop, City_Id]
- "INSERT INTO test (name, population) VALUES ($1, $2)", [City_Name, City_Pop]
- "INSERT INTO test (name, population) VALUES ($1, $2) RETURNING id", [City_Name, City_Pop]
- "DELETE FROM test WHERE id = $1", [City_Id]
- */
- void test(HTTPServerRequest req, HTTPServerResponse res){
- auto cache = memcachedConnect("127.0.0.1:11211");
-
-
-
- /* test unproper arguments order */
- /* do not */
- /*
- test_key1 x = 5;
- test_key2 y = 2;
- //string r1 = test_args_types_mismash(x, y); // proper arguments order
- string r1 = test_args_types_mismash(y, x); // unproper - this compiles but we got logic error bug in runtime.. :( use struct for compiler check this..
- writeln("r1 = ", r1);
- */
-
- /* do not */
- /*
- test_key5 x = 5;
- test_key6 y = 2;
- //string r3 = test_args_types_mismash(x, y); // proper arguments order
- string r3 = test_args_types_mismash(y, x); // unproper - this compiles but we got logic error bug in runtime.. :( use struct for compiler check this..
- writeln("r3 = ", r3);
- */
-
- /* do like */
-
- test_key5 x = 5;
- test_key6 y = 2;
- string r3 = test_args_types_mismash(x, y); // proper arguments order
- //string r3 = test_args_types_mismash(y, x); // unproper - this not compiles
- writeln("r3 = ", r3);
-
- /* or - do like */
- test_key3 x2 = test_key3(5);
- test_key4 y2 = test_key4(2);
- string r2 = test_args_types_mismash2(x2, y2); // proper arguments order
- //string r2 = test_args_types_mismash2(y2, x2); // unproper - this not compiles
- writeln("r2 = ", r2);
-
-
-
- Language Lang = Language.uk;
- writeln("tr 1 = ", Tr(Lang, TKey.hello));
- writeln("tr 2 = ", Tr(Lang, TKey.welcome, ["username"], 0) );
- writeln("tr 3 = ", Tr(Lang, TKey.apples, [], 1) );
- writeln("tr 3 = ", Tr(Lang, TKey.apples, [], 2) ) ;
- writeln("tr 3 = ", Tr(Lang, TKey.apples, [], 5) );
- writeln("tr 4 = ", Tr(Lang, TKey.apples_n_oranges, ["6", "7"], 0) );
-
-
-
- writeln("get test1 = ", cache.get!string("test1"));
-
- string v1 = "value1 = 🔥🦀";
-
- if(cache.store("test1", v1) == RETURN_STATE.SUCCESS ){
- writeln("stored successfully");
- writeln("get stored: ", cache.get!string("test1") );
- }else{
- writeln("not stored");
- }
-
- string result = cache.get!string("test1");
- writeln("get test1 = ", result);
-
- writeln(cache.del("test1"));
-
-
- //test_pg_conn_driver();
- test_pg_conn_driver_queries();
-
-
- Mustache mustache2;
- auto context2 = new Mustache.Context;
- mustache2.path = "priv/folder2";
- mustache2.ext = "dtl";
-
- context2["param2"] = "blah blah blah ";
-
- Mustache mustache;
- auto context = new Mustache.Context;
- mustache.path = "priv";
- mustache.ext = "dtl";
-
- //context.useSection("boolean");
- //assert(mustache.renderString(" {{#boolean}}YES{{/boolean}}\n {{#boolean}}GOOD{{/boolean}}\n", context) == " YES\n GOOD\n");
-
- //{{escaped_html_tags}}
- //{{{not_escaped_html_tags}}}
-
- //{{#repo}}<b>{{name}}</b>{{/repo}}
- //{{^repo}}No repos :({{/repo}}
- // to
- //No repos :(
-
- context["lang"] = "en";
- context["number1"] = 42;
- context.useSection("maybe1");
-
- context["part1"] = mustache2.render("part1", context2);
- context["result1"] = "Hello, World!\n" ~ result;
-
- res.headers["Content-Type"] = "text/html; charset=utf-8";
-
- //res.writeBody("Hello, World!\n" ~ result);
- res.writeBody( mustache.render("main", context) );
- }
|