├── CHANGELOG.md ├── CONTRIBUTING.md ├── docs ├── public │ ├── favicon.ico │ ├── histoire.mp3 │ ├── opengraph.png │ ├── stackblitz.png │ ├── logo.svg │ └── histoire+percy.svg ├── .vitepress │ ├── theme │ │ ├── util │ │ │ └── dark.ts │ │ ├── DemoLinks.vue │ │ ├── style │ │ │ ├── vars.pcss │ │ │ └── index.pcss │ │ ├── index.js │ │ ├── DemoPreview.vue │ │ ├── SponsorButton.vue │ │ └── MeetTeam.vue │ └── config.js ├── reference │ ├── definition.md │ └── pronunciation.md └── index.md ├── src ├── routes │ └── definition │ │ ├── index.js │ │ └── get.definition.route.js ├── utils │ ├── constants.js │ ├── errors.js │ └── utils.js ├── modules │ └── wiktionary │ │ └── dictionary.js └── app.js ├── postcss.config.cjs ├── .editorconfig ├── vercel.json ├── LICENSE ├── package.json ├── tailwind.config.cjs ├── .gitignore └── README.md /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngocsangyem/freedictionaryapi/HEAD/docs/public/favicon.ico -------------------------------------------------------------------------------- /docs/public/histoire.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngocsangyem/freedictionaryapi/HEAD/docs/public/histoire.mp3 -------------------------------------------------------------------------------- /docs/public/opengraph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngocsangyem/freedictionaryapi/HEAD/docs/public/opengraph.png -------------------------------------------------------------------------------- /docs/public/stackblitz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngocsangyem/freedictionaryapi/HEAD/docs/public/stackblitz.png -------------------------------------------------------------------------------- /src/routes/definition/index.js: -------------------------------------------------------------------------------- 1 | const { getDefinition } = require('./get.definition.route'); 2 | 3 | module.exports = { getDefinition }; 4 | -------------------------------------------------------------------------------- /docs/.vitepress/theme/util/dark.ts: -------------------------------------------------------------------------------- 1 | import { useDark, useToggle } from '@vueuse/core' 2 | 3 | export const isDark = useDark({ valueDark: 'dark' }) 4 | export const toggleDark = useToggle(isDark) 5 | -------------------------------------------------------------------------------- /postcss.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [ 3 | require('postcss-import'), 4 | require('tailwindcss/nesting'), 5 | require('tailwindcss')('./tailwind.config.cjs'), 6 | require('autoprefixer'), 7 | ], 8 | } 9 | -------------------------------------------------------------------------------- /src/utils/constants.js: -------------------------------------------------------------------------------- 1 | const constants = { 2 | cambridge: { 3 | BASE_URL: 'https://dictionary.cambridge.org', 4 | DICT: '/dictionary/', 5 | PRON: '/pronunciation/', 6 | }, 7 | oxford: { 8 | BASE_URL: 'https://www.oxfordlearnersdictionaries.com', 9 | DICT: '/definition/english', 10 | }, 11 | lingua_robot: { 12 | BASE_URL: 'https://lingua-robot.p.rapidapi.com/language/v1/entries/en', 13 | } 14 | }; 15 | 16 | module.exports = constants; 17 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = tab 7 | indent_size = 4 8 | tab_width = 4 9 | end_of_line = lf 10 | insert_final_newline = true 11 | trim_trailing_whitespace = false 12 | quote_type = single 13 | 14 | [*.js] 15 | indent_style = tab 16 | indent_size = 4 17 | tab_width = 4 18 | 19 | [{package.json,.travis.yml}] 20 | indent_style = tab 21 | indent_size = 4 22 | tab_width = 4 23 | 24 | [**.md] 25 | insert_final_newline = false 26 | trim_trailing_whitespace = false 27 | -------------------------------------------------------------------------------- /vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 2, 3 | "builds": [ 4 | { 5 | "src": "src/app.js", 6 | "use": "@vercel/node" 7 | } 8 | ], 9 | "routes": [ 10 | { 11 | "src": "/api/(.*)", 12 | "headers": { 13 | "Access-Control-Allow-Origin": "*", 14 | "Access-Control-Allow-Headers": "Origin, X-Requested-With, Content-Type, Accept", 15 | "Access-Control-Allow-Credentials": "true", 16 | "Cache-Control": "public, max-age=31556952, immutable" 17 | }, 18 | "continue": true, 19 | "dest": "src/app.js" 20 | }, 21 | { 22 | "src": "/api/(.*)", 23 | "methods": [ 24 | "OPTIONS" 25 | ], 26 | "dest": "src/app.js" 27 | } 28 | ] 29 | } -------------------------------------------------------------------------------- /docs/.vitepress/theme/DemoLinks.vue: -------------------------------------------------------------------------------- 1 | 30 | -------------------------------------------------------------------------------- /src/modules/wiktionary/dictionary.js: -------------------------------------------------------------------------------- 1 | const errors = require('../../utils/errors'); 2 | const readline = require('readline'); 3 | const { createReadStream } = require('fs'); 4 | require('dotenv').config(); 5 | 6 | class Dictionary { 7 | constructor() { 8 | this.dictionaryData = new Map(); 9 | } 10 | 11 | async loadDictionary(filePath) { 12 | const fileStream = createReadStream(filePath); 13 | const rl = readline.createInterface({ 14 | input: fileStream, 15 | crlfDelay: Infinity 16 | }); 17 | 18 | for await (const line of rl) { 19 | const entry = JSON.parse(line); 20 | const [word, definitions] = Object.entries(entry)[0]; 21 | this.dictionaryData.set(word, definitions); 22 | } 23 | 24 | console.log('Dictionary loaded successfully'); 25 | } 26 | } 27 | 28 | module.exports = Dictionary; 29 | -------------------------------------------------------------------------------- /docs/.vitepress/theme/style/vars.pcss: -------------------------------------------------------------------------------- 1 | :root { 2 | --c-brand: #10B981; 3 | --c-brand-light:#34D399; 4 | } 5 | 6 | html.dark { 7 | --c-brand: #3aa675; 8 | --c-brand-light: #349469; 9 | 10 | --c-bg: #22272e; 11 | --c-bg-light: #2b313a; 12 | --c-bg-lighter: #262c34; 13 | 14 | --c-text: #adbac7; 15 | --c-text-light: #96a7b7; 16 | --c-text-lighter: #8b9eb0; 17 | --c-text-lightest: #8094a8; 18 | 19 | --c-border: #3e4c5a; 20 | --c-border-dark: #34404c; 21 | --c-divider: #34404c; 22 | 23 | --c-tip: #318a62; 24 | --c-warning: #ceab00; 25 | --c-warning-bg: #7e755b; 26 | --c-warning-title: #ceac03; 27 | --c-warning-text: #362e00; 28 | --c-danger: #940000; 29 | --c-danger-bg: #806161; 30 | --c-danger-title: #610000; 31 | --c-danger-text: #3a0000; 32 | --c-details-bg: #323843; 33 | 34 | --code-hl-bg-color: #363b46; 35 | } 36 | -------------------------------------------------------------------------------- /docs/.vitepress/theme/index.js: -------------------------------------------------------------------------------- 1 | import DefaultTheme from 'vitepress/theme' 2 | import FloatingVue from 'floating-vue' 3 | import { Icon } from '@iconify/vue' 4 | import 'floating-vue/dist/style.css' 5 | import './style/vars.pcss' 6 | import './style/index.pcss' 7 | import SponsorButton from './SponsorButton.vue' 8 | import MeetTeam from './MeetTeam.vue' 9 | import DemoPreview from './DemoPreview.vue' 10 | import DemoLinks from './DemoLinks.vue' 11 | 12 | export default { 13 | ...DefaultTheme, 14 | enhanceApp ({ app }) { 15 | app.use(FloatingVue, { 16 | themes: { 17 | dropdown: { 18 | computeTransformOrigin: true, 19 | }, 20 | }, 21 | }) 22 | app.component('Icon', Icon) 23 | app.component('SponsorButton', SponsorButton) 24 | app.component('MeetTeam', MeetTeam) 25 | app.component('DemoPreview', DemoPreview) 26 | app.component('DemoLinks', DemoLinks) 27 | }, 28 | } 29 | -------------------------------------------------------------------------------- /docs/reference/definition.md: -------------------------------------------------------------------------------- 1 | # Definition 2 | 3 | ## Path 4 | 5 | | Location | Endpoint | 6 | | :---------------------------------------- |:---------------------------------| 7 | | | /api/[version] | 8 | 9 | ## Query 10 | 11 | | | Type | Description | Required | Default | 12 | | :-------- | :----- | :----------------------------- | :------- | :------ | 13 | | word | String | The word to search for | Yes | | 14 | | version | String | The version of the API to use | Yes | v1 | 15 | 16 | ## Methods 17 | 18 | | | Endpoint | Description | Example | 19 | | :-------- | :--------------------| :------------------------------------ | :------------------------- | 20 | | `GET` | `/entries/en/[word]` | Definitions of the given word | /api/v1/entries/en/go | 21 | -------------------------------------------------------------------------------- /docs/reference/pronunciation.md: -------------------------------------------------------------------------------- 1 | # Pronunciation 2 | 3 | ::: warning 4 | This doc is work-in-progress. Expect changes. 5 | ::: 6 | 7 | ## Path 8 | 9 | | Location | Endpoint | 10 | | :---------------------------------------- |:---------------------------------| 11 | | | /api/[version] | 12 | 13 | ## Query 14 | 15 | | | Type | Description | Required | Default | 16 | | :-------- | :----- | :----------------------------- | :------- | :------ | 17 | | word | String | The word to search for | Yes | | 18 | | version | String | The version of the API to use | Yes | v1 | 19 | 20 | ## Methods 21 | 22 | | | Endpoint | Description | Example | 23 | | :-------- | :--------------------------| :-------------------------------------- | :--------------------------------| 24 | | `GET` | `/pronunciation/en/[word]` | Pronunciation of the given word | /api/v1/pronunciation/en/go | 25 | 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2021 Sang Nguyen 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: home 3 | 4 | hero: 5 | name: Free Dictionary API 6 | text: A new way to write stories 7 | tagline: Powered by Vite 8 | image: 9 | src: /logo.svg 10 | alt: Free Dictionary API logo 11 | actions: 12 | - theme: brand 13 | text: Get Started 14 | link: /guide/getting-started 15 | - theme: alt 16 | text: Why Histoire? 17 | link: /guide/ 18 | - theme: alt 19 | text: View on GitHub 20 | link: https://github.com/ngocsangyem/freedictionaryapi 21 | 22 | features: 23 | - title: 📖 Stories 24 | details: Write stories to showcase and document your components. 25 | - title: ⚡ Fast 26 | details: Incredibly fast development building and production page loading! 27 | - title: 🔧️ No-config 28 | details: Sane and configurable defaults, automatically reuses your Vite config! 29 | - title: 🎨 Themable 30 | details: Customize the look of the generated app with your own branding. 31 | - title: 💻️ Copiable code 32 | details: Automatically generates dynamic template source code! 33 | - title: 🌙 Dark mode 34 | details: Enjoy a more pleasing experience during night. 35 | --- 36 | 37 | -------------------------------------------------------------------------------- /src/app.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const rateLimit = require('express-rate-limit'); 3 | const { getDefinition } = require('./routes/definition'); 4 | const WikiDictionary = require('./modules/wiktionary/dictionary'); 5 | const path = require('path'); 6 | 7 | const app = express(); 8 | let dictionary; 9 | 10 | const limiter = rateLimit({ 11 | windowMs: 5 * 60 * 1000, // 5 minutes 12 | max: 800, // limit each IP to 450 requests per windowMs 13 | }); 14 | const host = '0.0.0.0' 15 | const PORT = process.env.PORT || 3000; 16 | 17 | 18 | async function initializeDictionary() { 19 | try { 20 | const WikDictionary = new WikiDictionary(); 21 | await WikDictionary.loadDictionary(path.join(__dirname, '../data/data.jsonl')); 22 | 23 | dictionary = WikDictionary.dictionaryData; 24 | } catch (error) { 25 | console.error('Error loading dictionary:', error); 26 | process.exit(1); 27 | } 28 | } 29 | 30 | app.set('trust proxy', true); 31 | app.use(limiter); 32 | 33 | initializeDictionary().then(() => { 34 | app.use((req, res, next) => { 35 | req.dictionary = dictionary; 36 | next(); 37 | }); 38 | 39 | app.use('/api', getDefinition); 40 | 41 | app.listen(PORT, host, () => { 42 | console.log('Server is running on port 3000'); 43 | }); 44 | }); 45 | 46 | module.exports = app; 47 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "freedictionaryapi", 3 | "version": "1.0.0", 4 | "description": "Dictionary for EDict extension", 5 | "main": "src/app.js", 6 | "scripts": { 7 | "start": "node ./src/app.js", 8 | "docs:dev": "vitepress dev docs", 9 | "docs:build": "vitepress build docs", 10 | "docs:serve": "vitepress serve docs" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/ngocsangyem/freedictionaryapi.git" 15 | }, 16 | "keywords": [ 17 | "API", 18 | "Nodejs", 19 | "Javascript" 20 | ], 21 | "author": "ngocsangyem", 22 | "license": "MIT", 23 | "bugs": { 24 | "url": "https://github.com/ngocsangyem/freedictionaryapi/issues" 25 | }, 26 | "homepage": "https://github.com/ngocsangyem/freedictionaryapi#readme", 27 | "dependencies": { 28 | "axios": "^0.27.2", 29 | "cheerio": "^1.0.0-rc.10", 30 | "dotenv": "^16.0.3", 31 | "event-stream": "^4.0.1", 32 | "express": "^4.17.3", 33 | "express-rate-limit": "^6.3.0", 34 | "jsdom": "^19.0.0", 35 | "mongodb": "^5.1.0", 36 | "node-fetch": "^2.6.7" 37 | }, 38 | "devDependencies": { 39 | "@iconify/vue": "^3.2.1", 40 | "@vueuse/core": "^8.9.0", 41 | "autoprefixer": "^10.4.7", 42 | "floating-vue": "^2.0.0-beta.17", 43 | "postcss": "^8.4.14", 44 | "sass": "^1.53.0", 45 | "tailwindcss": "^3.1.4", 46 | "vitepress": "^1.0.0-alpha.4", 47 | "vue": "^3.2.37" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /docs/.vitepress/theme/DemoPreview.vue: -------------------------------------------------------------------------------- 1 |