Browse Source

update mit api

burningTyger 5 years ago
parent
commit
bcc4ba9aa6

+ 80 - 2
package-lock.json

@@ -1,6 +1,6 @@
 {
-  "name": "TODO",
-  "version": "0.0.1",
+  "name": "farhang",
+  "version": "3.0.0",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {
@@ -915,6 +915,15 @@
       "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
       "dev": true
     },
+    "better-sqlite3": {
+      "version": "5.4.1",
+      "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-5.4.1.tgz",
+      "integrity": "sha512-uFIjBdnSbzZT8NEqgH1IuARJaA4wtbJ2VmM/ibxlkY+9a0Z8dlyJZBBeKTg2l8L8fPvfpXearuZbjtr+5x8MiA==",
+      "requires": {
+        "integer": "^2.1.0",
+        "tar": "^4.4.6"
+      }
+    },
     "brace-expansion": {
       "version": "1.1.11",
       "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -983,6 +992,11 @@
         "supports-color": "^5.3.0"
       }
     },
+    "chownr": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.2.tgz",
+      "integrity": "sha512-GkfeAQh+QNy3wquu9oIZr6SS5x7wGdSgNQvD10X3r+AZr1Oys22HW8kAmDMvNg2+Dm0TeGaEuO8gFwdBXxwO8A=="
+    },
     "clean-css": {
       "version": "4.2.1",
       "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz",
@@ -1170,6 +1184,14 @@
       "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
       "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="
     },
+    "fs-minipass": {
+      "version": "1.2.6",
+      "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.6.tgz",
+      "integrity": "sha512-crhvyXcMejjv3Z5d2Fa9sf5xLYVCF5O1c71QxbVnbLsmYMBEvDAftewesN/HhY03YRoA7zOMxjNGrF5svGaaeQ==",
+      "requires": {
+        "minipass": "^2.2.1"
+      }
+    },
     "function-bind": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
@@ -1246,6 +1268,11 @@
       "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
       "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
     },
+    "integer": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/integer/-/integer-2.1.0.tgz",
+      "integrity": "sha512-vBtiSgrEiNocWvvZX1RVfeOKa2mCHLZQ2p9nkQkQZ/BvEiY+6CcUz0eyjvIiewjJoeNidzg2I+tpPJvpyspL1w=="
+    },
     "invariant": {
       "version": "2.2.4",
       "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
@@ -1463,6 +1490,38 @@
       "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
       "dev": true
     },
+    "minipass": {
+      "version": "2.3.5",
+      "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz",
+      "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==",
+      "requires": {
+        "safe-buffer": "^5.1.2",
+        "yallist": "^3.0.0"
+      }
+    },
+    "minizlib": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.2.1.tgz",
+      "integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==",
+      "requires": {
+        "minipass": "^2.2.1"
+      }
+    },
+    "mkdirp": {
+      "version": "0.5.1",
+      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
+      "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
+      "requires": {
+        "minimist": "0.0.8"
+      },
+      "dependencies": {
+        "minimist": {
+          "version": "0.0.8",
+          "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+          "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
+        }
+      }
+    },
     "ms": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
@@ -2016,6 +2075,20 @@
       "integrity": "sha512-2N9kIbDal5z/aZloaRCOQ9dlCtuCE08NZITDlSdG7fOl4kFrE2qnXCq+lSFtI15ABWCXPc17cX5vJvdOgUsKqw==",
       "dev": true
     },
+    "tar": {
+      "version": "4.4.10",
+      "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.10.tgz",
+      "integrity": "sha512-g2SVs5QIxvo6OLp0GudTqEf05maawKUxXru104iaayWA09551tFCTI8f1Asb4lPfkBr91k07iL4c11XO3/b0tA==",
+      "requires": {
+        "chownr": "^1.1.1",
+        "fs-minipass": "^1.2.5",
+        "minipass": "^2.3.5",
+        "minizlib": "^1.2.1",
+        "mkdirp": "^0.5.0",
+        "safe-buffer": "^5.1.2",
+        "yallist": "^3.0.3"
+      }
+    },
     "terser": {
       "version": "4.1.2",
       "resolved": "https://registry.npmjs.org/terser/-/terser-4.1.2.tgz",
@@ -2133,6 +2206,11 @@
       "requires": {
         "isexe": "^2.0.0"
       }
+    },
+    "yallist": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz",
+      "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A=="
     }
   }
 }

+ 1 - 0
package.json

@@ -12,6 +12,7 @@
     "test": "run-p --race dev cy:run"
   },
   "dependencies": {
+    "better-sqlite3": "^5.4.1",
     "bulma": "^0.7.5",
     "compression": "^1.7.1",
     "polka": "next",

+ 24 - 0
src/routes/a/[term].js

@@ -0,0 +1,24 @@
+const DB_PATH = process.env.DB_PATH;
+const db = require('better-sqlite3')(DB_PATH);
+
+export async function get(req, res, next) {
+  const { term } = req.params;
+  if (!term) next()
+  const items = db
+    .prepare(`SELECT id, lemma
+              FROM lemmas
+              WHERE lemma
+                LIKE ?
+              ORDER BY lemma
+                COLLATE NOCASE ASC
+            `)
+    .all(`${term}%`)
+	if (items.length>0) {
+    res.setHeader("Access-Control-Allow-Origin", "*");
+		res.setHeader('Content-Type', 'application/json');
+		res.end(JSON.stringify(items));
+	} else {
+    res.writeHead(404, { 'Content-Type': 'application/json' });
+    res.end();
+	}
+}

+ 20 - 0
src/routes/g/[id].js

@@ -0,0 +1,20 @@
+const DB_PATH = process.env.DB_PATH;
+const db = require('better-sqlite3')(DB_PATH);
+
+export async function get(req, res, next) {
+  const { id } = req.params;
+  if (!id) next()
+  const item = db
+    .prepare(`SELECT source, target
+              FROM translations
+              WHERE lemma_id = ?
+            `)
+    .all(id)
+	if (item) {
+    res.setHeader("Access-Control-Allow-Origin", "*");
+		res.setHeader('Content-Type', 'application/json');
+		res.end(JSON.stringify(item));
+	} else {
+		next();
+	}
+}

+ 104 - 61
src/routes/index.svelte

@@ -1,30 +1,44 @@
+<script context="module">
+	export async function preload(page, session) {
+		const res = await this.fetch(`stats.json`);
+		const stats = await res.json();
+		return { stats };
+	}
+</script>
+
 <script>
-    let term = ''
-    let lemmas = []
-    let translations = []
-    let selectedItem
-    let persian
-    $:  if (term.length > 1) {
-        persian = checkPersian(term)
-        const normalized = persian ? term.replace(/([\u064B-\u0655])/g, '') : term
-        const uri = encodeURI(`https://api.farhang.im/a/${normalized}`)
-        fetch(uri)
-						.then(res => res.json())
-            .then(json => setLemmas(json))
-    }
-    $: if (term.length < 2) lemmas = []
-    function setLemmas(res) { lemmas = res }
-    async function showLemma (id) {
-			translations = []
-      selectedItem = id;
-      const uri = encodeURI(`https://api.farhang.im/g/${selectedItem}`)
-      const res = await fetch(uri)
-			translations = await res.json()
-    }
-    function checkPersian (string) {
-        const regex = /[\u0622\u0627\u0628\u067E\u062A-\u0632\u0686\u0698\u0633-\u063A\u0641\u0642\u06A9\u06AF\u0644-\u0648\u06CC]/
-        return regex.test(string)
-    }
+	export let stats
+	let term = ''
+	let lemmas = []
+	let translations = []
+	let selected_item
+	let persian
+	$:  if (term.length > 1) {
+			persian = is_persian(term)
+			const normalized = persian ? term.replace(/([\u064B-\u0655])/g, '') : term
+			const uri = encodeURI(`a/${normalized}`)
+			fetch(uri)
+				.then(res => {
+					if (res.ok) return res.json()
+					else throw "nichts gefunden"
+				})
+				.then(json => set_lemmas(json))
+				.catch(e => lemmas=[{id:null,lemma:e}])
+	}
+	$: if (term.length < 2) lemmas = []
+	function set_lemmas(res) { lemmas = res }
+	async function show_lemma (id) {
+		if (!id) return
+		translations = []
+		selected_item = id;
+		const uri = encodeURI(`g/${selected_item}`)
+		const res = await fetch(uri)
+		translations = await res.json()
+	}
+	function is_persian (string) {
+			const regex = /[\u0622\u0627\u0628\u067E\u062A-\u0632\u0686\u0698\u0633-\u063A\u0641\u0642\u06A9\u06AF\u0644-\u0648\u06CC]/
+			return regex.test(string)
+	}
 </script>
 
 <style>
@@ -33,17 +47,31 @@
   text-decoration: none;
   color: #333; }
 .hero-body, .subtitle { transition: padding 0.5s;}
+ul {
+  list-style: none;
+	margin-top: 0;
+}
+.card-content {padding:0.5em;}
+ul li::before {
+  content: "\200B";
+}
+.paddingfix {
+	padding: 0 .3em 1em .3em !important;
+}
+.full-height {
+	min-height: 100vh;
+}
 </style>
 
 <link href="//fonts.googleapis.com/css?family=Overlock:900italic" rel="stylesheet" type="text/css"/>
 <svelte:head><title>Farhang</title></svelte:head>
-<section class="hero is-info is-bold is-large">
-	<div class={lemmas.length>0 ? 'hero-body is-paddingless':'hero-body'}>
+<section class="hero is-info is-bold full-height">
+	<div class:paddingfix={lemmas.length>0} class="hero-body">
 		<div class="container has-text-centered">
-			<p class="">
+			<p>
 				<span class="brand is-size-1">Farhang</span><br>
 			</p>
-			<p class={lemmas.length>0 ? 'subtitle is-hidden':'subtitle'}>
+			<p class={lemmas.length>0 ? 'is-hidden':'subtitle'}>
 				<span class="is-size-4">deutsch-persisches Lexikon</span>
 			</p>
 			<div class="columns is-centered">
@@ -53,9 +81,7 @@
 			</div>
 			{#if lemmas<1}
 				<p class="is-size-5 has-text-left-mobile">
-					Mit 31629 Einträgen und 80192 Übersetzungen ist <span class="brand">Farhang</span>
-					eines der größten deutsch-persischen Wörterbücher, die online verfügbar sind. Dazu sind alle
-					Einträge frei zugänglich und können beliebig verwendet werden.
+					Aktuell sind {stats.lemmas} Einträge und {stats.translations} Übersetzungen in der Datenbank
 				</p>
 				<p class="is-size-5 has-text-right-mobile">
 					فرهنگ فارسی - آلمانی
@@ -71,34 +97,51 @@
 				</a>
 			{/if}
 		</div>
-	</div>
-	{#if lemmas<1}
-		<div class="hero-foot">Some footer stuff</div>
-	{/if}
-</section>
-{#if lemmas.length > 0}
-	<div class="columns is-centered">
-		<div class="column is-half">
-				{#each lemmas as l}
-				<div class="card">
-					<header class="card-header">
-						<p class="card-header-title" on:click={()=>showLemma(l.id)}>{l.lemma}</p>
-					</header>
-					{#if l.id === selectedItem}
-						<div class="card-content">
-							<div class="content">
-								<ul>
-									{#each translations as t}
-										<li>
-											{t.source} - {t.target}
-										</li>
-									{/each}
-								</ul>
-							</div>
+		{#if lemmas.length > 0}
+			<div class="columns is-centered">
+				<div class="column is-half">
+					{#each lemmas as l}
+						<div class={`card ${persian ? 'has-text-right':''}`}>
+							<header class="card-header">
+								<p class="card-header-title" style="cursor: pointer" on:click={()=>show_lemma(l.id)}>{l.lemma}</p>
+							</header>
+							{#if l.id === selected_item}
+								<div class="card-content">
+									<div class="content">
+										<ul style={persian ? 'margin-left:0; margin-right:1em':''}>
+											{#each translations as t}
+												<li>
+													{#if persian}
+														{t.target} - {t.source}
+													{:else}
+														{t.source} - {t.target}
+													{/if}
+												</li>
+											{/each}
+										</ul>
+									</div>
+								</div>
+							{/if}
 						</div>
-					{/if}
+					{/each}
 				</div>
-				{/each}
-		</div>
+			</div>
+		{/if}
 	</div>
-{/if}
+		<div class="hero-foot is-size-7">
+			<nav class="level has-text-grey-darker">
+					<div class="level-item has-text-centered">
+						<div>
+							<p>
+								<a href="https://creativecommons.org/licenses/by-sa/4.0/deed.de">
+									<img alt="Creative Commons Lizenzvertrag" src="by-sa.png">
+								</a>
+							</p>
+							<p>
+								Farhang.im ©&nbsp;<a class="has-text-grey-darker" href="https://github.com/ckh/farhang">ckh</a>&nbsp;&&nbsp;<a class="has-text-grey-darker" href="https://github.com/hmt">hmt</a>
+							</p>
+						</div>
+					</div>
+			</nav>
+		</div>
+</section>

+ 18 - 0
src/routes/stats.json.js

@@ -0,0 +1,18 @@
+const DB_PATH = process.env.DB_PATH;
+const db = require('better-sqlite3')(DB_PATH);
+
+export async function get(req, res, next) {
+  const stats = db
+    .prepare(`SELECT
+                (SELECT COUNT(DISTINCT id) FROM translations) AS translations,
+                (SELECT COUNT(DISTINCT id) FROM lemmas) AS lemmas
+            `)
+    .all()
+	if (stats.length>0) {
+    res.setHeader("Access-Control-Allow-Origin", "*");
+		res.setHeader('Content-Type', 'application/json');
+		res.end(JSON.stringify(stats[0]));
+	} else {
+		next();
+	}
+}

BIN
static/by-sa.png


BIN
static/de_badge_web_generic.png