app.d 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  1. alias uint8 = ubyte;
  2. //import vibe.vibe;
  3. // https://github.com/vibe-d/vibe.d/blob/master/source/vibe/vibe.d
  4. import vibe.core.core;
  5. import vibe.http.router;
  6. import vibe.http.server;
  7. import vibe.http.fileserver;
  8. import vibe.http.websockets;
  9. import vibe.core.log;
  10. import std.stdio;
  11. import std.string;
  12. //import std.array;
  13. import memcached4d;
  14. import std.conv : to;
  15. import tr;
  16. import mustache;
  17. alias MustacheEngine!(string) Mustache;
  18. import vibe.db.postgresql;
  19. import vibe.data.bson;
  20. //import vibe.data.json;
  21. // https://github.com/vibe-d/vibe.d/blob/master/source/vibe/vibe.d
  22. PostgresClient[] clients;
  23. import std.file : read;
  24. import toml;
  25. TOMLDocument toml_s;
  26. // settings keys
  27. string s_toml_http = "http";
  28. string s_toml_http_host = "host";
  29. string s_toml_http_port = "port";
  30. string s_toml_db = "database";
  31. string s_toml_db_host = "host";
  32. string s_toml_db_port = "port";
  33. string s_toml_db_name = "dbname";
  34. string s_toml_db_user = "user";
  35. string s_toml_db_pass = "pass";
  36. string s_toml_db_conn_timeout = "connect_timeout";
  37. string s_toml_db_conn_num = "connections_number";
  38. /* test unproper arguments order */
  39. /* do not */
  40. /*
  41. alias test_key1 = int;
  42. alias test_key2 = int;
  43. string test_args_types_mismash(test_key1 x, test_key2 y){
  44. return ( to!string(x) ~ " + " ~ to!string(y) ~ " = " ~ to!string( x + y ) );
  45. }
  46. */
  47. /* do not */
  48. /*
  49. import std.typecons : Typedef;
  50. alias test_key5 = Typedef!int;
  51. alias test_key6 = Typedef!int;
  52. string test_args_types_mismash(test_key5 x, test_key6 y){
  53. return ( to!string(x) ~ " + " ~ to!string(y) ~ " = " ~ to!string( x + y ) );
  54. }
  55. */
  56. /* do like */
  57. import std.typecons : Typedef;
  58. alias test_key5 = Typedef!(int, int.init, "key5");
  59. alias test_key6 = Typedef!(int, int.init, "key6");
  60. string test_args_types_mismash(test_key5 x, test_key6 y){
  61. return ( to!string(x) ~ " + " ~ to!string(y) ~ " = " ~ to!string( x + y ) );
  62. }
  63. /* or - do like */
  64. struct test_key3 { int v; }
  65. struct test_key4 { int v; }
  66. string test_args_types_mismash2(test_key3 x, test_key4 y){
  67. return ( to!string(x.v) ~ " + " ~ to!string(y.v) ~ " = " ~ to!string( x.v + y.v ) );
  68. }
  69. void main(){
  70. toml_s = parseTOML(cast(string)read("settings.toml"));
  71. if( are_valid_config_values(toml_s) ){}else{ return; } // settings validation
  72. uint conn_num = cast(uint) toml_s[s_toml_db][s_toml_db_conn_num].integer();
  73. uint8 i = cast(uint8) conn_num;
  74. // https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING
  75. // https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-PARAMKEYWORDS
  76. //client = new PostgresClient("host=localhost port=5432 dbname=mydb user=username password=pass connect_timeout=5", 100);
  77. while(i > 0){
  78. //writeln("i = ", i);
  79. clients ~= new PostgresClient( "host=" ~ toml_s[s_toml_db][s_toml_db_host].str() ~
  80. " port=" ~ toml_s[s_toml_db][s_toml_db_port].str() ~
  81. " dbname=" ~ toml_s[s_toml_db][s_toml_db_name].str() ~
  82. " user=" ~ toml_s[s_toml_db][s_toml_db_user].str() ~
  83. " password=" ~ toml_s[s_toml_db][s_toml_db_pass].str() ~
  84. " connect_timeout=" ~ toml_s[s_toml_db][s_toml_db_conn_timeout].str(),
  85. conn_num,
  86. (scope Connection conn){
  87. conn.prepareEx("get_city_by_id", "SELECT id, name, population FROM test WHERE id = $1");
  88. } );
  89. i--;
  90. }
  91. auto settings = new HTTPServerSettings;
  92. //settings.port = 8080;
  93. //settings.bindAddresses = ["::1", "127.0.0.1"];
  94. settings.port = cast(ushort)toml_s[s_toml_http][s_toml_http_port].integer();
  95. settings.bindAddresses = [ toml_s[s_toml_http][s_toml_http_host].str().idup ];
  96. //auto fsettings = new HTTPFileServerSettings;
  97. //fsettings.serverPathPrefix = "/static";
  98. auto router = new URLRouter;
  99. //router.get("/", &index);
  100. ////router.get("static/*", serverStaticFiles("public/", fsettings) );
  101. //router.get("/", staticTemplate!"index.html");
  102. router.get("/", serveStaticFile("public/index.html") );
  103. router.get("/ws", handleWebSockets(&ws_handle) );
  104. router.get("/test", &test);
  105. router.get("*", serveStaticFiles("public/"));
  106. //auto listener = listenHTTP(settings, &hello);
  107. auto listener = listenHTTP(settings, router);
  108. scope (exit){
  109. listener.stopListening();
  110. }
  111. logInfo("Please open http://127.0.0.1:8080/ in your browser.");
  112. runApplication();
  113. }
  114. void ws_handle(scope WebSocket sock){
  115. // simple echo server + :)
  116. while(sock.connected){
  117. auto msg = sock.receiveText();
  118. sock.send(msg ~ " :)");
  119. }
  120. }
  121. /*
  122. void index(HTTPServerRequest req, HTTPServerResponse res){
  123. res.writeBody("Hello, World!");
  124. }
  125. */
  126. /*
  127. void hello(HTTPServerRequest req, HTTPServerResponse res){
  128. res.writeBody("Hello, World!");
  129. }
  130. */
  131. bool are_valid_config_values(ref TOMLDocument toml_s){ // settings validation
  132. string invalid_settings = "invalid settings: ";
  133. string grumpy = " :(";
  134. string invalid_group = " group ";
  135. string invalid_key = " key" ~ grumpy;
  136. bool invalid_toml_group(string group){
  137. writeln(invalid_settings ~ group ~ invalid_group ~ grumpy); return false;
  138. }
  139. bool invalid_toml_value(string group, string key){
  140. writeln(invalid_settings ~ group ~ invalid_group ~ key ~ invalid_key ~ grumpy); return false;
  141. }
  142. if((s_toml_http in toml_s) != null){
  143. auto toml_http = toml_s[s_toml_http];
  144. if((s_toml_http_host in toml_http) != null){
  145. if(toml_http[s_toml_http_host].type == TOMLType.STRING){}else{ return invalid_toml_value(s_toml_http, s_toml_http_host); }
  146. }else{ return invalid_toml_value(s_toml_http, s_toml_http_host); }
  147. if((s_toml_http_port in toml_http) != null){
  148. if(toml_http[s_toml_http_port].type == TOMLType.INTEGER){}else{ return invalid_toml_value(s_toml_http, s_toml_http_port); }
  149. }else{ return invalid_toml_value(s_toml_http, s_toml_http_port); }
  150. }else{ return invalid_toml_group(s_toml_http); }
  151. if((s_toml_db in toml_s) != null){
  152. auto toml_db = toml_s[s_toml_db];
  153. if((s_toml_db_host in toml_db) != null){
  154. if(toml_db[s_toml_db_host].type == TOMLType.STRING){}else{ return invalid_toml_value(s_toml_db, s_toml_db_host); }
  155. }else{ return invalid_toml_value(s_toml_db, s_toml_db_host); }
  156. if((s_toml_db_port in toml_db) != null){
  157. if(toml_db[s_toml_db_port].type == TOMLType.STRING){}else{ return invalid_toml_value(s_toml_db, s_toml_db_port); }
  158. }else{ return invalid_toml_value(s_toml_db, s_toml_db_port); }
  159. if((s_toml_db_name in toml_db) != null){
  160. if(toml_db[s_toml_db_name].type == TOMLType.STRING){}else{ return invalid_toml_value(s_toml_db, s_toml_db_name); }
  161. }else{ return invalid_toml_value(s_toml_db, s_toml_db_name); }
  162. if((s_toml_db_user in toml_db) != null){
  163. if(toml_db[s_toml_db_user].type == TOMLType.STRING){}else{ return invalid_toml_value(s_toml_db, s_toml_db_user); }
  164. }else{ return invalid_toml_value(s_toml_db, s_toml_db_user); }
  165. if((s_toml_db_pass in toml_db) != null){
  166. if(toml_db[s_toml_db_pass].type == TOMLType.STRING){}else{ return invalid_toml_value(s_toml_db, s_toml_db_pass); }
  167. }else{ return invalid_toml_value(s_toml_db, s_toml_db_pass); }
  168. if((s_toml_db_conn_timeout in toml_db) != null){
  169. if(toml_db[s_toml_db_conn_timeout].type == TOMLType.STRING){}else{ return invalid_toml_value(s_toml_db, s_toml_db_conn_timeout); }
  170. }else{ return invalid_toml_value(s_toml_db, s_toml_db_conn_timeout); }
  171. if((s_toml_db_conn_num in toml_db) != null){
  172. if(toml_db[s_toml_db_conn_num].type == TOMLType.INTEGER){}else{ return invalid_toml_value(s_toml_db, s_toml_db_conn_num); }
  173. }else{ return invalid_toml_value(s_toml_db, s_toml_db_conn_num); }
  174. }else{ return invalid_toml_group(s_toml_db); }
  175. return true;
  176. }
  177. /*
  178. void test_pg_conn_driver(){ // https://github.com/denizzzka/vibe.d.db.postgresql/blob/master/example/example.d#L13
  179. client.pickConnection( (scope conn){
  180. immutable result = conn.exec(
  181. "SELECT 123 as first_num, 567 as second_num, 'abc'::text as third_text " ~
  182. "UNION ALL " ~
  183. "SELECT 890, 233, 'fgh'::text as third_text",
  184. ValueFormat.BINARY
  185. );
  186. assert(result[0]["second_num"].as!PGinteger == 567);
  187. assert(result[1]["third_text"].as!PGtext == "fgh");
  188. foreach (val; rangify(result[0])){
  189. writeln("Found entry: ", val.as!Bson.toJson);
  190. }
  191. } );
  192. }
  193. */
  194. string get_all_cities(){
  195. return "SELECT id, name, population FROM test ORDER BY id";
  196. }
  197. void test_pg_conn_driver_queries(){
  198. /*
  199. client.pickConnection( (scope conn){
  200. QueryParams params; // https://github.com/denizzzka/dpq2/blob/master/src/dpq2/args.d#L15
  201. params.preparedStatementName = "get_city_by_id";
  202. 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
  203. conn.prepareEx(params.preparedStatementName, "SELECT id, name, population FROM test WHERE id = $1"); // get_city_by_id
  204. auto result1 = conn.execPrepared(params);
  205. writeln("id: ", result1[0]["id"].as!PGinteger);
  206. writeln("name: ", result1[0]["name"].as!PGtext);
  207. writeln("population: ", result1[0]["population"].as!PGinteger);
  208. //conn.prepareEx("q1", "UPDATE test SET name = $1, population = $2 WHERE id = $3"); // update_city_by_id
  209. //immutable result1 = conn.execPrepared("", ValueFormat.BINARY);
  210. destroy(conn);
  211. } );
  212. */
  213. //auto conn = clients[0].lockConnection(); // todo think is this correct to take index 0 here
  214. clients[0].pickConnection( (scope conn){ // todo think is this correct to take index 0 here
  215. QueryParams params; // https://github.com/denizzzka/dpq2/blob/master/src/dpq2/args.d#L15
  216. params.preparedStatementName = "get_city_by_id";
  217. 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
  218. //conn.prepareEx(params.preparedStatementName, "SELECT id, name, population FROM test WHERE id = $1"); // get_city_by_id
  219. auto result1 = conn.execPrepared(params);
  220. writeln("id: ", result1[0]["id"].as!PGinteger);
  221. writeln("name: ", result1[0]["name"].as!PGtext);
  222. writeln("population: ", result1[0]["population"].as!PGinteger);
  223. //conn.prepareEx("q1", "UPDATE test SET name = $1, population = $2 WHERE id = $3"); // update_city_by_id
  224. //immutable result1 = conn.execPrepared("", ValueFormat.BINARY);
  225. destroy(conn);
  226. } );
  227. }
  228. /*
  229. "SELECT id, name, population FROM test ORDER BY id"
  230. "UPDATE test SET name = $1, population = $2 WHERE id = $3", [City_Name, City_Pop, City_Id]
  231. "INSERT INTO test (name, population) VALUES ($1, $2)", [City_Name, City_Pop]
  232. "INSERT INTO test (name, population) VALUES ($1, $2) RETURNING id", [City_Name, City_Pop]
  233. "DELETE FROM test WHERE id = $1", [City_Id]
  234. */
  235. void test(HTTPServerRequest req, HTTPServerResponse res){
  236. auto cache = memcachedConnect("127.0.0.1:11211");
  237. /* test unproper arguments order */
  238. /* do not */
  239. /*
  240. test_key1 x = 5;
  241. test_key2 y = 2;
  242. //string r1 = test_args_types_mismash(x, y); // proper arguments order
  243. 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..
  244. writeln("r1 = ", r1);
  245. */
  246. /* do not */
  247. /*
  248. test_key5 x = 5;
  249. test_key6 y = 2;
  250. //string r3 = test_args_types_mismash(x, y); // proper arguments order
  251. 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..
  252. writeln("r3 = ", r3);
  253. */
  254. /* do like */
  255. test_key5 x = 5;
  256. test_key6 y = 2;
  257. string r3 = test_args_types_mismash(x, y); // proper arguments order
  258. //string r3 = test_args_types_mismash(y, x); // unproper - this not compiles
  259. writeln("r3 = ", r3);
  260. /* or - do like */
  261. test_key3 x2 = test_key3(5);
  262. test_key4 y2 = test_key4(2);
  263. string r2 = test_args_types_mismash2(x2, y2); // proper arguments order
  264. //string r2 = test_args_types_mismash2(y2, x2); // unproper - this not compiles
  265. writeln("r2 = ", r2);
  266. Language Lang = Language.uk;
  267. writeln("tr 1 = ", Tr(Lang, TKey.hello));
  268. writeln("tr 2 = ", Tr(Lang, TKey.welcome, ["username"], 0) );
  269. writeln("tr 3 = ", Tr(Lang, TKey.apples, [], 1) );
  270. writeln("tr 3 = ", Tr(Lang, TKey.apples, [], 2) ) ;
  271. writeln("tr 3 = ", Tr(Lang, TKey.apples, [], 5) );
  272. writeln("tr 4 = ", Tr(Lang, TKey.apples_n_oranges, ["6", "7"], 0) );
  273. writeln("get test1 = ", cache.get!string("test1"));
  274. string v1 = "value1 = 🔥🦀";
  275. if(cache.store("test1", v1) == RETURN_STATE.SUCCESS ){
  276. writeln("stored successfully");
  277. writeln("get stored: ", cache.get!string("test1") );
  278. }else{
  279. writeln("not stored");
  280. }
  281. string result = cache.get!string("test1");
  282. writeln("get test1 = ", result);
  283. writeln(cache.del("test1"));
  284. //test_pg_conn_driver();
  285. test_pg_conn_driver_queries();
  286. Mustache mustache2;
  287. auto context2 = new Mustache.Context;
  288. mustache2.path = "priv/folder2";
  289. mustache2.ext = "dtl";
  290. context2["param2"] = "blah blah blah ";
  291. Mustache mustache;
  292. auto context = new Mustache.Context;
  293. mustache.path = "priv";
  294. mustache.ext = "dtl";
  295. //context.useSection("boolean");
  296. //assert(mustache.renderString(" {{#boolean}}YES{{/boolean}}\n {{#boolean}}GOOD{{/boolean}}\n", context) == " YES\n GOOD\n");
  297. //{{escaped_html_tags}}
  298. //{{{not_escaped_html_tags}}}
  299. //{{#repo}}<b>{{name}}</b>{{/repo}}
  300. //{{^repo}}No repos :({{/repo}}
  301. // to
  302. //No repos :(
  303. context["lang"] = "en";
  304. context["number1"] = 42;
  305. context.useSection("maybe1");
  306. context["part1"] = mustache2.render("part1", context2);
  307. context["result1"] = "Hello, World!\n" ~ result;
  308. res.headers["Content-Type"] = "text/html; charset=utf-8";
  309. //res.writeBody("Hello, World!\n" ~ result);
  310. res.writeBody( mustache.render("main", context) );
  311. }