├── .gitignore ├── README.md ├── app.vue ├── archive └── manga │ └── [titleID].ts ├── assets ├── css │ └── main.css ├── data.ts ├── fonts │ ├── Roboto-Bold.ttf │ ├── Roboto-BoldItalic.ttf │ ├── Roboto-Italic.ttf │ └── Roboto-Regular.ttf └── images │ ├── logo_kana.png │ └── logo_latin.png ├── components ├── Card │ ├── BookcaseCard.vue │ ├── DefaultCard.vue │ ├── ListsCard.vue │ ├── PersonMangaCard.vue │ └── RelationCard.vue ├── Layout │ ├── Footer.vue │ ├── MobileNavbar.vue │ └── Navbar.vue └── Swiper │ ├── AutoSwiper.vue │ ├── FreeSwiper.vue │ ├── HeroSwiper.vue │ ├── LastsSwiper.vue │ └── Recommendations.vue ├── content └── articles │ ├── about.md │ └── terms.md ├── errorx.vue ├── layouts ├── default.vue └── mobile.vue ├── nuxt.config.ts ├── package.json ├── pages ├── [...slug].vue ├── index.vue ├── person │ └── [personID].vue ├── scan │ └── [scanID] │ │ └── index.vue ├── search.vue ├── title │ └── [titleID] │ │ ├── index.vue │ │ └── read │ │ └── [key].vue └── user │ └── [userID] │ ├── index.vue │ ├── library.vue │ └── list │ └── [listID].vue ├── pnpm-lock.yaml ├── public ├── favicon.ico └── robots.txt ├── server ├── api │ ├── index.ts │ ├── title │ │ └── [titleID] │ │ │ ├── index.ts │ │ │ └── read │ │ │ └── [key].ts │ └── users │ │ └── [userID].ts └── tsconfig.json └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Nuxt dev/build outputs 2 | .output 3 | .data 4 | .nuxt 5 | .nitro 6 | .cache 7 | dist 8 | 9 | # Node dependencies 10 | node_modules 11 | 12 | # Logs 13 | logs 14 | *.log 15 | 16 | # Misc 17 | .DS_Store 18 | .fleet 19 | .idea 20 | 21 | # Local env files 22 | .env 23 | .env.* 24 | !.env.example 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mangile RC 2 | 3 | It will be the last version before stable one, 4 | 5 | Although the `errorx.vue` is actually `error.vue`, it is not used during rc for debugging. 6 | 7 | The Website URL: [mangile.vercel.app](https://mangile.vercel.app) (There are various known problems in the mobile view) 8 | 9 | | Development Stage | Manhwa Reader | Novel Reader | Manga Reader | Profile Actions | Bookcase Actions | List Actions | Comments | Notifications | Advanced SEO & Meta | 10 | | ----------------- | ------------- | ------------ | ------------ | --------------- | ---------------- | ------------ | -------- | ------------- | ------------------- | 11 | | Stage 1 |✅ |✅ |✅ | | | | | | | 12 | | Stage 2 [...] |✅ |✅ |✅ | | | ✓ | ✓ | | | 13 | | Stage 3 |✅ |✅ |✅ | ✓ | ✓ | ✓ | ✓ | | | 14 | | Stage 4 |✅ |✅ |✅ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | 15 | 16 | Profile Actions: Edit Profile, Delete Profile, Reading History 17 | 18 | Bookcase Actions: Add, Edit, Remove 19 | 20 | List Actions: Create, Add, Edit, Remove, Delete 21 | 22 | Comments: Disqus 23 | 24 | Notifications: Advanced websocket notification system 25 | 26 | 27 | Stage 1 is completed. This means that you can read the content you want without any difficulty. But user actions are still in early access. 28 | 29 | Follow releases and projects sections for new updates. If you encounter some problems, you can create an issue or a pull request. 30 | 31 | ### Why should you prefer Mangile? 32 | 33 | There are too many Turkish manga/webtoon/novel scans and they are NOT connected to each other at all. This is exactly what Mangile aims for. Mangile aims to gather all Turkish translations of these series under itself and offer them to the user from a single source. So, instead of typing "read x in turkish" on the search engine and reading it from a site that the users are not even sure whether they can read without any problems,they can easily get their work done from Mangile's search bar. In addition, Mangile presents the chapters without any problem and in "official publishing order", which makes it easy to track. Mangile also has a tracking system* where the users can track their series. This allows users to favorite the series, add* them to their lists, and update* their reading status. Users can comment* on series, share them, or request new series to be added. Mangile also has a reader that can be easily used on both desktop and mobile devices. The system provides this reader for manga, webtoon and novels. These are the reasons why you should prefer Mangile. 34 | 35 | *: not yet possible (planned) -------------------------------------------------------------------------------- /app.vue: -------------------------------------------------------------------------------- 1 | 20 | 32 | 51 | -------------------------------------------------------------------------------- /archive/manga/[titleID].ts: -------------------------------------------------------------------------------- 1 | import { createCanvas, loadImage, registerFont } from "canvas"; 2 | import { send } from "h3"; 3 | import path from "path"; 4 | 5 | registerFont( 6 | path.join(process.cwd(), "assets", "fonts", "Roboto-Regular.ttf"), 7 | { 8 | family: "Roboto", 9 | weight: "normal", 10 | } 11 | ); 12 | registerFont(path.join(process.cwd(), "assets", "fonts", "Roboto-Bold.ttf"), { 13 | family: "Roboto", 14 | weight: "bold", 15 | }); 16 | registerFont(path.join(process.cwd(), "assets", "fonts", "Roboto-Italic.ttf"), { 17 | family: "Roboto", 18 | style: "italic", 19 | }); 20 | 21 | function wrapText( 22 | ctx: any, 23 | text: string, 24 | x: number, 25 | y: number, 26 | maxWidth: number, 27 | lineHeight: number 28 | ) { 29 | const words = text.split(" "); 30 | let line = ""; 31 | let lineY = y; 32 | 33 | for (let i = 0; i < words.length; i++) { 34 | const testLine = line + words[i] + " "; 35 | const testWidth = ctx.measureText(testLine).width; 36 | 37 | if (testWidth > maxWidth && line !== "") { 38 | ctx.fillText(line.trim(), x, lineY); 39 | line = words[i] + " "; 40 | lineY += lineHeight; 41 | } else { 42 | line = testLine; 43 | } 44 | } 45 | 46 | if (line) { 47 | ctx.fillText(line.trim(), x, lineY); 48 | } 49 | } 50 | 51 | export default defineEventHandler(async (event) => { 52 | const canvas = createCanvas(1200, 630); 53 | const ctx = canvas.getContext("2d"); 54 | const mangaID = getRouterParam(event, "titleID"); 55 | 56 | const mangaData = await fetch( 57 | `https://api.jikan.moe/v4/manga/${mangaID}/full` 58 | ).then((response) => response.json()); 59 | 60 | const imageUrl = mangaData.data.images.jpg.large_image_url; 61 | const image = await loadImage(imageUrl); // Arka plan 62 | const image2 = await loadImage(imageUrl); // Kapak 63 | 64 | ctx.fillStyle = "black"; 65 | ctx.fillRect(0, 0, canvas.width, canvas.height); 66 | 67 | // Resmi kırpma 68 | const { width: imgWidth, height: imgHeight } = image; 69 | const targetWidth = 1200; 70 | const targetHeight = 630; 71 | 72 | const scale = Math.max(targetWidth / imgWidth, targetHeight / imgHeight); 73 | const newWidth = imgWidth * scale; 74 | const newHeight = imgHeight * scale; 75 | const offsetX = (newWidth - targetWidth) / 2; 76 | const offsetY = (newHeight - targetHeight) / 2; 77 | 78 | ctx.globalAlpha = 0.33; 79 | ctx.drawImage(image, -offsetX, -offsetY, newWidth, newHeight); 80 | 81 | ctx.globalAlpha = 1; 82 | const coverWidth = 421; 83 | const coverHeight = 600; 84 | const coverX = canvas.width - coverWidth - 20; 85 | const coverY = canvas.height - coverHeight - 20; 86 | 87 | try { 88 | ctx.drawImage(image2, coverX, coverY, coverWidth, coverHeight); 89 | } catch (error) { 90 | console.error("Kapak resmi çizilirken bir hata oluştu:", error); 91 | } 92 | 93 | ctx.font = "bold 64px Roboto"; 94 | ctx.fillStyle = "white"; 95 | ctx.textAlign = "left"; 96 | ctx.textBaseline = "top"; 97 | 98 | const title = mangaData.data.title; 99 | ctx.fillText( 100 | title.length >= 16 ? title.substring(0, 16) + "..." : title, 101 | 75, 102 | 75 103 | ); 104 | 105 | ctx.font = "italic 32px Roboto"; 106 | const author = mangaData.data.authors[0]?.name || "Bilinmiyor"; 107 | ctx.fillText(author, 75, 135); 108 | 109 | ctx.font = "16px Roboto"; 110 | const synopsis = mangaData.data.synopsis || "Mangaya Mangile'da göz at!"; 111 | const maxWidth = 600; 112 | const lineHeight = 24; 113 | 114 | wrapText( 115 | ctx, 116 | synopsis.length >= 500 ? synopsis.substring(0, 500) + "..." : synopsis, 117 | 75, 118 | 250, 119 | maxWidth, 120 | lineHeight 121 | ); 122 | 123 | ctx.font = "bold 16px Roboto"; 124 | ctx.fillText("mangile.vercel.app | created by falsisdev", 75, 600); 125 | 126 | const imageData = canvas.toBuffer("image/png"); 127 | 128 | return send(event, imageData, "image/png"); 129 | }); 130 | -------------------------------------------------------------------------------- /assets/css/main.css: -------------------------------------------------------------------------------- 1 | @import "tailwindcss"; 2 | @plugin "@tailwindcss/typography"; 3 | @plugin "daisyui" { 4 | themes: all; 5 | } -------------------------------------------------------------------------------- /assets/data.ts: -------------------------------------------------------------------------------- 1 | export const data = { 2 | status: [ 3 | "Okunuyor", 4 | "Tamamlandı", 5 | "Beklemede", 6 | "Bırakıldı", 7 | "Planlandı", 8 | "Yeniden Okunuyor", 9 | ], 10 | themeselect: { 11 | sunset: { 12 | id: "sunset", 13 | title: "Gün Batımı", 14 | scheme: "dark", 15 | status: "default", 16 | }, 17 | dark: { 18 | id: "geist", 19 | title: "Karanlık", 20 | scheme: "dark", 21 | status: "recommended", 22 | }, 23 | lofi: { 24 | id: "lofi", 25 | title: "Aydınlık", 26 | scheme: "light", 27 | status: "recommended", 28 | }, 29 | black: { 30 | id: "black", 31 | title: "Karanlık", 32 | scheme: "dark", 33 | status: "amoled", 34 | }, 35 | dracula: { 36 | id: "dracula", 37 | title: "Dracula", 38 | scheme: "dark", 39 | status: "recommended", 40 | }, 41 | dim: { 42 | id: "dim", 43 | title: "Tan", 44 | scheme: "dark", 45 | status: "recommended", 46 | }, 47 | night: { 48 | id: "night", 49 | title: "Gece", 50 | scheme: "dark", 51 | status: "recommended", 52 | }, 53 | retro: { 54 | id: "retro", 55 | title: "Retro", 56 | scheme: "light", 57 | status: null, 58 | }, 59 | forest: { 60 | id: "forest", 61 | title: "Orman", 62 | scheme: "dark", 63 | status: null, 64 | }, 65 | halloween: { 66 | id: "halloween", 67 | title: "Bal Kabağı", 68 | scheme: "dark", 69 | status: null, 70 | }, 71 | emerald: { 72 | id: "emerald", 73 | title: "Zümrüt", 74 | scheme: "light", 75 | status: null, 76 | }, 77 | valentine: { 78 | id: "valentine", 79 | title: "Sevgi", 80 | scheme: "light", 81 | status: null, 82 | }, 83 | synthwave: { 84 | id: "synthwave", 85 | title: "Gelecek", 86 | scheme: "dark", 87 | status: null, 88 | }, 89 | cyberpunk: { 90 | id: "cyberpunk", 91 | title: "Cyberpunk", 92 | scheme: "light", 93 | status: null, 94 | }, 95 | pastel: { 96 | id: "pastel", 97 | title: "Pastel", 98 | scheme: "light", 99 | status: null, 100 | }, 101 | wireframe: { 102 | id: "wireframe", 103 | title: "Manga", 104 | scheme: "light", 105 | status: null, 106 | }, 107 | luxury: { 108 | id: "luxury", 109 | title: "Lüks", 110 | scheme: "dark", 111 | status: null, 112 | }, 113 | autumn: { 114 | id: "autumn", 115 | title: "Güz", 116 | scheme: "light", 117 | status: null, 118 | }, 119 | business: { 120 | id: "business", 121 | title: "İşkolik", 122 | scheme: "dark", 123 | status: null, 124 | }, 125 | coffee: { 126 | id: "coffee", 127 | title: "Kahve", 128 | scheme: "dark", 129 | status: null, 130 | }, 131 | }, 132 | statusMessages: { 133 | 400: "Kötü istek", 134 | 401: "Yetkisiz", 135 | 403: "Yasaklandı", 136 | 404: "Sayfa bulunamadı", 137 | 405: "Method izin verilmiyor", 138 | 409: "Çakışma", 139 | 429: "Çok fazla istek", 140 | 500: "Sunucu hatası", 141 | 501: "Uygulanamadı", 142 | 502: "Kötü ağ geçidi", 143 | 503: "Hizmet kullanılamıyor", 144 | 504: "Ağ geçidi zaman aşımına uğradı", 145 | }, 146 | warnmessages: { 147 | 58: "kan ve vahşet içerikleri", 148 | 28: "toplumun ahlakına zarar verebilecek içerikler", 149 | 26: "toplumun ahlakına zarar verebilecek içerikler", 150 | 9: "yetişkin içerikleri", 151 | 49: "yetişkin içerikleri", 152 | 12: "yetişkin içerikleri", 153 | 50: "yetişkin içerikleri", 154 | 44: "toplumun ahlakına zarar verebilecek içerikler", 155 | 35: "olumsuz örnek teşkil eden içerikler", 156 | 65: "yetişkin içerikleri", 157 | 74: "olumsuz örnek teşkil eden içerikler", 158 | }, 159 | titletypes: { 160 | Default: ["Varsayılan", null], 161 | Synonym: ["Anlamdaş", null], 162 | Japanese: ["Japonca", "jp"], 163 | English: ["İngilizce", "gb-eng"], 164 | French: ["Fransızca", "fr"], 165 | German: ["Almanca", "de"], 166 | Spanish: ["İspanyolca", "es"], 167 | Portugese: ["Portekizce", "pt"], 168 | Italian: ["İtalyanca", "it"], 169 | Dutch: ["Flemenkçe", "nl"], 170 | Russian: ["Rusça", "ru"], 171 | Turkish: ["Türkçe", "tr"], 172 | Azerbaijani: ["Azerbaycan Dili", "az"], 173 | Kazakh: ["Kazakça", "kz"], 174 | Uzbek: ["Özbekçe", "uz"], 175 | Chinese: ["Çince", "tw"], 176 | Korean: ["Korece", "kr"], 177 | Arabic: ["Arapça", "ly"], 178 | }, 179 | months: [ 180 | "Ocak", 181 | "Şubat", 182 | "Mart", 183 | "Nisan", 184 | "Mayıs", 185 | "Haziran", 186 | "Temmuz", 187 | "Ağustos", 188 | "Eylül", 189 | "Ekim", 190 | "Kasım", 191 | "Aralık", 192 | ], 193 | malstatus: { 194 | Finished: "Yayın Tamamlandı", 195 | Publishing: "Yayınlanıyor", 196 | Discontinued: "Yayın Durduruldu", 197 | "On Hiatus": "Beklemede", 198 | }, 199 | malgenres: { 200 | Action: "Aksiyon", 201 | Adventure: "Macera", 202 | "Avant Garde": "Avantgart", 203 | "Award Winning": "Ödüllü", 204 | "Boys Love": "Eril Eşcinsel", 205 | Comedy: "Komedi", 206 | Drama: "Dram", 207 | Fantasy: "Fantezi", 208 | "Girls Love": "Dişil Eşcinsel", 209 | Gourmet: "Gurme", 210 | Horror: "Korku", 211 | Mystery: "Gizem", 212 | Romance: "Romantik", 213 | "Sci-Fi": "Bilim Kurgu", 214 | "Slice of Life": "Yaşamdan Kesit", 215 | Sports: "Spor", 216 | Supernatural: "Doğaüstü", 217 | Suspense: "Gerilim", 218 | Ecchi: "Ecchi", 219 | Erotica: "Erotik", 220 | Hentai: "Hentai", 221 | "Adult Cast": "Yetişkin Karakter", 222 | Anthropomorphic: "Antropomorfik", 223 | CGDCT: "ŞKŞŞY", 224 | Childcare: "Çocuk Bakımı", 225 | "Combat Sports": "Dövüş Sporları", 226 | Crossdressing: "Travestilik", 227 | Delinquents: "Suçlular", 228 | Detective: "Dedektif", 229 | Educational: "Eğitici", 230 | "Gag Humor": "Mizah", 231 | Gore: "Vahşet", 232 | Harem: "Harem", 233 | "High Stakes Game": "Yüksek Bahisli Oyun", 234 | Historical: "Tarihsel", 235 | "Idols (Female)": "Dişil İdoller", 236 | "Idols (Male)": "Eril İdoller", 237 | Isekai: "Isekai", 238 | Iyashikei: "Iyashikei", 239 | "Love Polygon": "Aşk Çokgeni", 240 | "Magical Sex Shift": "Büyülü Cinsel Değişim", 241 | "Mahou Shoujo": "Büyülü Kız", 242 | "Martial Arts": "Dövüş Sanatları", 243 | Mecha: "Meka", 244 | Medical: "Tıbbi", 245 | Memoir: "Anısal", 246 | Military: "Askeri", 247 | Music: "Müzik", 248 | Mythology: "Mitoloji", 249 | "Organized Crime": "Organize Suç", 250 | "Otaku Culture": "Otaku Kültürü", 251 | Parody: "Parodi", 252 | "Performing Arts": "Sahne Sanatları", 253 | Pets: "Evcil Hayvanlar", 254 | Psychological: "Psikolojik", 255 | Racing: "Yarış", 256 | Reincarnation: "Reenkarnasyon", 257 | "Reverse Harem": "Ters Harem", 258 | "Romantic Subtext": "Romantik", 259 | Samurai: "Samuray", 260 | School: "Okul", 261 | Showbiz: "Şov Dünyası", 262 | Space: "Uzay", 263 | "Strategy Game": "Strateji Oyunu", 264 | "Super Power": "Süper Güç", 265 | Survival: "Hayatta Kalma", 266 | "Team Sports": "Takım Sporları", 267 | "Time Travel": "Zaman Yolculuğu", 268 | Vampire: "Vampir", 269 | "Video Game": "Video Oyunları", 270 | Villainess: "Düşmancıllık", 271 | "Visual Arts": "Görsel Sanatlar", 272 | Workplace: "İşyeri", 273 | Josei: "Josei", 274 | Kids: "Çocuklar", 275 | Seinen: "Seinen", 276 | Shoujo: "Shoujo", 277 | Shounen: "Shounen", 278 | }, 279 | themes: { 280 | aqua: { 281 | "color-scheme": "dark", 282 | primary: "#09ecf3", 283 | "primary-content": "#005355", 284 | secondary: "#966fb3", 285 | accent: "#ffe999", 286 | neutral: "#3b8ac4", 287 | "base-100": "#345da7", 288 | info: "#2563eb", 289 | success: "#16a34a", 290 | warning: "#d97706", 291 | error: "oklch(73.95% 0.19 27.33)", 292 | }, 293 | black: { 294 | "color-scheme": "dark", 295 | primary: "#373737", 296 | secondary: "#373737", 297 | accent: "#373737", 298 | "base-100": "#000000", 299 | "base-200": "#141414", 300 | "base-300": "#262626", 301 | "base-content": "#d6d6d6", 302 | neutral: "#373737", 303 | info: "#0000ff", 304 | success: "#008000", 305 | warning: "#ffff00", 306 | error: "#ff0000", 307 | "--rounded-box": "0", 308 | "--rounded-btn": "0", 309 | "--rounded-badge": "0", 310 | "--animation-btn": "0", 311 | "--animation-input": "0", 312 | "--btn-focus-scale": "1", 313 | "--tab-radius": "0", 314 | }, 315 | bumblebee: { 316 | "color-scheme": "light", 317 | primary: "oklch(89.51% 0.2132 96.61)", 318 | "primary-content": "oklch(38.92% 0.046 96.61)", 319 | secondary: "oklch(80.39% 0.194 70.76)", 320 | "secondary-content": "oklch(39.38% 0.068 70.76)", 321 | accent: "oklch(81.27% 0.157 56.52)", 322 | neutral: "oklch(12.75% 0.075 281.99)", 323 | "base-100": "oklch(100% 0 0)", 324 | }, 325 | cmyk: { 326 | "color-scheme": "light", 327 | primary: "#45AEEE", 328 | secondary: "#E8488A", 329 | accent: "#FFF232", 330 | neutral: "#1a1a1a", 331 | "base-100": "oklch(100% 0 0)", 332 | info: "#4AA8C0", 333 | success: "#823290", 334 | warning: "#EE8133", 335 | error: "#E93F33", 336 | }, 337 | corporate: { 338 | "color-scheme": "light", 339 | primary: "oklch(60.39% 0.228 269.1)", 340 | secondary: "#7b92b2", 341 | accent: "#67cba0", 342 | neutral: "#181a2a", 343 | "neutral-content": "#edf2f7", 344 | "base-100": "oklch(100% 0 0)", 345 | "base-content": "#181a2a", 346 | "--rounded-box": "0.25rem", 347 | "--rounded-btn": ".125rem", 348 | "--rounded-badge": ".125rem", 349 | "--tab-radius": "0.25rem", 350 | "--animation-btn": "0", 351 | "--animation-input": "0", 352 | "--btn-focus-scale": "1", 353 | }, 354 | cupcake: { 355 | "color-scheme": "light", 356 | primary: "#65c3c8", 357 | secondary: "#ef9fbc", 358 | accent: "#eeaf3a", 359 | neutral: "#291334", 360 | "base-100": "#faf7f5", 361 | "base-200": "#efeae6", 362 | "base-300": "#e7e2df", 363 | "base-content": "#291334", 364 | "--rounded-btn": "1.9rem", 365 | "--tab-border": "2px", 366 | "--tab-radius": "0.7rem", 367 | }, 368 | cyberpunk: { 369 | "color-scheme": "light", 370 | fontFamily: 371 | "ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace", 372 | primary: "oklch(74.22% 0.209 6.35)", 373 | secondary: "oklch(83.33% 0.184 204.72)", 374 | accent: "oklch(71.86% 0.2176 310.43)", 375 | neutral: "oklch(23.04% 0.065 269.31)", 376 | "neutral-content": "oklch(94.51% 0.179 104.32)", 377 | "base-100": "oklch(94.51% 0.179 104.32)", 378 | "--rounded-box": "0", 379 | "--rounded-btn": "0", 380 | "--rounded-badge": "0", 381 | "--tab-radius": "0", 382 | }, 383 | dark: { 384 | "color-scheme": "dark", 385 | primary: "oklch(65.69% 0.196 275.75)", 386 | secondary: "oklch(74.8% 0.26 342.55)", 387 | accent: "oklch(74.51% 0.167 183.61)", 388 | neutral: "#2a323c", 389 | "neutral-content": "#A6ADBB", 390 | "base-100": "#1d232a", 391 | "base-200": "#191e24", 392 | "base-300": "#15191e", 393 | "base-content": "#A6ADBB", 394 | }, 395 | dracula: { 396 | "color-scheme": "dark", 397 | primary: "#ff79c6", 398 | secondary: "#bd93f9", 399 | accent: "#ffb86c", 400 | neutral: "#414558", 401 | "base-100": "#282a36", 402 | "base-content": "#f8f8f2", 403 | info: "#8be9fd", 404 | success: "#50fa7b", 405 | warning: "#f1fa8c", 406 | error: "#ff5555", 407 | }, 408 | emerald: { 409 | "color-scheme": "light", 410 | primary: "#66cc8a", 411 | "primary-content": "#223D30", 412 | secondary: "#377cfb", 413 | "secondary-content": "#fff", 414 | accent: "#f68067", 415 | "accent-content": "#000", 416 | neutral: "#333c4d", 417 | "neutral-content": "#f9fafb", 418 | "base-100": "oklch(100% 0 0)", 419 | "base-content": "#333c4d", 420 | "--animation-btn": "0", 421 | "--animation-input": "0", 422 | "--btn-focus-scale": "1", 423 | }, 424 | fantasy: { 425 | "color-scheme": "light", 426 | primary: "oklch(37.45% 0.189 325.02)", 427 | secondary: "oklch(53.92% 0.162 241.36)", 428 | accent: "oklch(75.98% 0.204 56.72)", 429 | neutral: "#1f2937", 430 | "base-100": "oklch(100% 0 0)", 431 | "base-content": "#1f2937", 432 | }, 433 | forest: { 434 | "color-scheme": "dark", 435 | primary: "#addfad", 436 | secondary: "#e779c1", 437 | accent: "#818cf8", 438 | neutral: "#23282e", 439 | "base-100": "#1c1917", 440 | warning: "#ff865b", 441 | info: "#89e0eb", 442 | "--rounded-btn": "0.5rem", 443 | "--rounded-badge": "0.5rem", 444 | }, 445 | garden: { 446 | "color-scheme": "light", 447 | primary: "oklch(62.45% 0.278 3.8363600743192197)", 448 | "primary-content": "#fff", 449 | secondary: "#8E4162", 450 | accent: "#5c7f67", 451 | neutral: "#291E00", 452 | "neutral-content": "#e9e7e7", 453 | "base-100": "#e9e7e7", 454 | "base-content": "#100f0f", 455 | }, 456 | halloween: { 457 | "color-scheme": "dark", 458 | primary: "oklch(77.48% 0.204 60.62)", 459 | "primary-content": "#131616", 460 | secondary: "oklch(45.98% 0.248 305.03)", 461 | accent: "oklch(64.8% 0.223 136.07347934356451)", 462 | "accent-content": "#000000", 463 | neutral: "#2F1B05", 464 | "base-100": "#212121", 465 | info: "#2563eb", 466 | success: "#16a34a", 467 | warning: "#d97706", 468 | error: "oklch(65.72% 0.199 27.33)", 469 | }, 470 | light: { 471 | "color-scheme": "light", 472 | primary: "oklch(49.12% 0.3096 275.75)", 473 | secondary: "oklch(69.71% 0.329 342.55)", 474 | "secondary-content": "oklch(98.71% 0.0106 342.55)", 475 | accent: "oklch(76.76% 0.184 183.61)", 476 | neutral: "#2B3440", 477 | "neutral-content": "#D7DDE4", 478 | "base-100": "oklch(100% 0 0)", 479 | "base-200": "#F2F2F2", 480 | "base-300": "#E5E6E6", 481 | "base-content": "#1f2937", 482 | }, 483 | lofi: { 484 | "color-scheme": "light", 485 | primary: "#0D0D0D", 486 | "primary-content": "oklch(100% 0 0)", 487 | secondary: "#1A1919", 488 | "secondary-content": "oklch(100% 0 0)", 489 | accent: "#262626", 490 | "accent-content": "oklch(100% 0 0)", 491 | neutral: "#000000", 492 | "neutral-content": "oklch(100% 0 0)", 493 | "base-100": "oklch(100% 0 0)", 494 | "base-200": "#F2F2F2", 495 | "base-300": "#E6E5E5", 496 | "base-content": "#000000", 497 | info: "oklch(79.54% 0.103 205.9)", 498 | success: "oklch(90.13% 0.153 164.14)", 499 | warning: "oklch(88.37% 0.135 79.94)", 500 | error: "oklch(78.66% 0.15 28.47)", 501 | "--rounded-box": "0.25rem", 502 | "--rounded-btn": "0.125rem", 503 | "--rounded-badge": "0.125rem", 504 | "--tab-radius": "0.125rem", 505 | "--animation-btn": "0", 506 | "--animation-input": "0", 507 | "--btn-focus-scale": "1", 508 | }, 509 | luxury: { 510 | "color-scheme": "dark", 511 | primary: "oklch(100% 0 0)", 512 | secondary: "#152747", 513 | accent: "#513448", 514 | neutral: "#331800", 515 | "neutral-content": "#FFE7A3", 516 | "base-100": "#09090b", 517 | "base-200": "#171618", 518 | "base-300": "#2e2d2f", 519 | "base-content": "#dca54c", 520 | info: "#66c6ff", 521 | success: "#87d039", 522 | warning: "#e2d562", 523 | error: "#ff6f6f", 524 | }, 525 | pastel: { 526 | "color-scheme": "light", 527 | primary: "#d1c1d7", 528 | secondary: "#f6cbd1", 529 | accent: "#b4e9d6", 530 | neutral: "#70acc7", 531 | "base-100": "oklch(100% 0 0)", 532 | "base-200": "#f9fafb", 533 | "base-300": "#d1d5db", 534 | "--rounded-btn": "1.9rem", 535 | "--tab-radius": "0.7rem", 536 | }, 537 | retro: { 538 | "color-scheme": "light", 539 | primary: "#ef9995", 540 | "primary-content": "#282425", 541 | secondary: "#a4cbb4", 542 | "secondary-content": "#282425", 543 | accent: "#DC8850", 544 | "accent-content": "#282425", 545 | neutral: "#2E282A", 546 | "neutral-content": "#EDE6D4", 547 | "base-100": "#ece3ca", 548 | "base-200": "#e4d8b4", 549 | "base-300": "#DBCA9A", 550 | "base-content": "#282425", 551 | info: "#2563eb", 552 | success: "#16a34a", 553 | warning: "#d97706", 554 | error: "oklch(65.72% 0.199 27.33)", 555 | "--rounded-box": "0.4rem", 556 | "--rounded-btn": "0.4rem", 557 | "--rounded-badge": "0.4rem", 558 | "--tab-radius": "0.4rem", 559 | }, 560 | synthwave: { 561 | "color-scheme": "dark", 562 | primary: "#e779c1", 563 | secondary: "#58c7f3", 564 | accent: "oklch(88.04% 0.206 93.72)", 565 | neutral: "#221551", 566 | "neutral-content": "#f9f7fd", 567 | "base-100": "#1a103d", 568 | "base-content": "#f9f7fd", 569 | info: "#53c0f3", 570 | "info-content": "#201047", 571 | success: "#71ead2", 572 | "success-content": "#201047", 573 | warning: "#eace6c", 574 | "warning-content": "#201047", 575 | error: "#ec8c78", 576 | "error-content": "#201047", 577 | }, 578 | valentine: { 579 | "color-scheme": "light", 580 | primary: "#e96d7b", 581 | secondary: "#a991f7", 582 | accent: "#66b1b3", 583 | neutral: "#af4670", 584 | "neutral-content": "#f0d6e8", 585 | "base-100": "#fae7f4", 586 | "base-content": "#632c3b", 587 | info: "#2563eb", 588 | success: "#16a34a", 589 | warning: "#d97706", 590 | error: "oklch(73.07% 0.207 27.33)", 591 | "--rounded-btn": "1.9rem", 592 | "--tab-radius": "0.7rem", 593 | }, 594 | wireframe: { 595 | "color-scheme": "light", 596 | fontFamily: "Chalkboard,comic sans ms,'sans-serif'", 597 | primary: "#b8b8b8", 598 | secondary: "#b8b8b8", 599 | accent: "#b8b8b8", 600 | neutral: "#ebebeb", 601 | "base-100": "oklch(100% 0 0)", 602 | "base-200": "#eeeeee", 603 | "base-300": "#dddddd", 604 | info: "#0000ff", 605 | success: "#008000", 606 | warning: "#a6a659", 607 | error: "#ff0000", 608 | "--rounded-box": "0.2rem", 609 | "--rounded-btn": "0.2rem", 610 | "--rounded-badge": "0.2rem", 611 | "--tab-radius": "0.2rem", 612 | }, 613 | autumn: { 614 | "color-scheme": "light", 615 | primary: "#8C0327", 616 | secondary: "#D85251", 617 | accent: "#D59B6A", 618 | neutral: "#826A5C", 619 | "base-100": "#f1f1f1", 620 | info: "#42ADBB", 621 | success: "#499380", 622 | warning: "#E97F14", 623 | error: "oklch(53.07% 0.241 24.16)", 624 | }, 625 | business: { 626 | "color-scheme": "dark", 627 | primary: "#1C4E80", 628 | secondary: "#7C909A", 629 | accent: "#EA6947", 630 | neutral: "#23282E", 631 | "base-100": "#202020", 632 | info: "#0091D5", 633 | success: "#6BB187", 634 | warning: "#DBAE59", 635 | error: "#AC3E31", 636 | "--rounded-box": "0.25rem", 637 | "--rounded-btn": ".125rem", 638 | "--rounded-badge": ".125rem", 639 | }, 640 | acid: { 641 | "color-scheme": "light", 642 | primary: "oklch(71.9% 0.357 330.7595734057481)", 643 | secondary: "oklch(73.37% 0.224 48.25087840015526)", 644 | accent: "oklch(92.78% 0.264 122.96295065960891)", 645 | neutral: "oklch(21.31% 0.128 278.68)", 646 | "base-100": "#fafafa", 647 | info: "oklch(60.72% 0.227 252.05)", 648 | success: "oklch(85.72% 0.266 158.53)", 649 | warning: "oklch(91.01% 0.212 100.5)", 650 | error: "oklch(64.84% 0.293 29.34918758658804)", 651 | "--rounded-box": "1.25rem", 652 | "--rounded-btn": "1rem", 653 | "--rounded-badge": "1rem", 654 | "--tab-radius": "0.7rem", 655 | }, 656 | lemonade: { 657 | "color-scheme": "light", 658 | primary: "oklch(58.92% 0.199 134.6)", 659 | secondary: "oklch(77.75% 0.196 111.09)", 660 | accent: "oklch(85.39% 0.201 100.73)", 661 | neutral: "oklch(30.98% 0.075 108.6)", 662 | "base-100": "oklch(98.71% 0.02 123.72)", 663 | info: "oklch(86.19% 0.047 224.14)", 664 | success: "oklch(86.19% 0.047 157.85)", 665 | warning: "oklch(86.19% 0.047 102.15)", 666 | error: "oklch(86.19% 0.047 25.85)", 667 | }, 668 | night: { 669 | "color-scheme": "dark", 670 | primary: "#38bdf8", 671 | secondary: "#818CF8", 672 | accent: "#F471B5", 673 | neutral: "#1E293B", 674 | "base-100": "#0F172A", 675 | info: "#0CA5E9", 676 | "info-content": "#000000", 677 | success: "#2DD4BF", 678 | warning: "#F4BF50", 679 | error: "#FB7085", 680 | }, 681 | coffee: { 682 | "color-scheme": "dark", 683 | primary: "#DB924B", 684 | secondary: "#263E3F", 685 | accent: "#10576D", 686 | neutral: "#120C12", 687 | "base-100": "#20161F", 688 | "base-content": "#c59f60", 689 | info: "#8DCAC1", 690 | success: "#9DB787", 691 | warning: "#FFD25F", 692 | error: "#FC9581", 693 | }, 694 | winter: { 695 | "color-scheme": "light", 696 | primary: "oklch(56.86% 0.255 257.57)", 697 | secondary: "#463AA2", 698 | accent: "#C148AC", 699 | neutral: "#021431", 700 | "base-100": "oklch(100% 0 0)", 701 | "base-200": "#F2F7FF", 702 | "base-300": "#E3E9F4", 703 | "base-content": "#394E6A", 704 | info: "#93E7FB", 705 | success: "#81CFD1", 706 | warning: "#EFD7BB", 707 | error: "#E58B8B", 708 | }, 709 | dim: { 710 | "color-scheme": "dark", 711 | primary: "#9FE88D", 712 | secondary: "#FF7D5C", 713 | accent: "#C792E9", 714 | neutral: "#1c212b", 715 | "neutral-content": "#B2CCD6", 716 | "base-100": "#2A303C", 717 | "base-200": "#242933", 718 | "base-300": "#20252E", 719 | "base-content": "#B2CCD6", 720 | info: "#28ebff", 721 | success: "#62efbd", 722 | warning: "#efd057", 723 | error: "#ffae9b", 724 | }, 725 | nord: { 726 | "color-scheme": "light", 727 | primary: "#5E81AC", 728 | secondary: "#81A1C1", 729 | accent: "#88C0D0", 730 | neutral: "#4C566A", 731 | "neutral-content": "#D8DEE9", 732 | "base-100": "#ECEFF4", 733 | "base-200": "#E5E9F0", 734 | "base-300": "#D8DEE9", 735 | "base-content": "#2E3440", 736 | info: "#B48EAD", 737 | success: "#A3BE8C", 738 | warning: "#EBCB8B", 739 | error: "#BF616A", 740 | "--rounded-box": "0.4rem", 741 | "--rounded-btn": "0.2rem", 742 | "--rounded-badge": "0.4rem", 743 | "--tab-radius": "0.2rem", 744 | }, 745 | sunset: { 746 | "color-scheme": "dark", 747 | primary: "#FF865B", 748 | secondary: "#FD6F9C", 749 | accent: "#B387FA", 750 | neutral: "oklch(26% 0.019 237.69)", 751 | "neutral-content": "oklch(70% 0.019 237.69)", 752 | "base-100": "oklch(22% 0.019 237.69)", 753 | "base-200": "oklch(20% 0.019 237.69)", 754 | "base-300": "oklch(18% 0.019 237.69)", 755 | "base-content": "#9fb9d0", 756 | info: "#89e0eb", 757 | success: "#addfad", 758 | warning: "#f1c891", 759 | error: "#ffbbbd", 760 | "--rounded-box": "1.2rem", 761 | "--rounded-btn": "0.8rem", 762 | "--rounded-badge": "0.4rem", 763 | "--tab-radius": "0.7rem", 764 | }, 765 | geist: { 766 | primary: "#ffffff", 767 | "primary-content": "#000000", 768 | secondary: "#10233d", 769 | "secondary-content": "#3e80c6", 770 | accent: "#2e1938", 771 | "accent-content": "#8b57af", 772 | info: "#0057ff", 773 | "info-content": "#ffffff", 774 | success: "#429e53", 775 | "success-content": "#e5fbea", 776 | warning: "#291800", 777 | "warning-content": "#8b5a05", 778 | error: "#d32e34", 779 | "error-content": "#ff6166", 780 | neutral: "#242424", 781 | "neutral-content": "#a1a1a1", 782 | "base-100": "#1a1a1a", 783 | "base-200": "#1f1f1f", 784 | "base-300": "#292929", 785 | "base-content": "#ededed", 786 | "--rounded-box": "1em", 787 | "--rounded-btn": "0.5rem", 788 | "--rounded-badge": "0.5rem", 789 | }, 790 | }, 791 | }; 792 | -------------------------------------------------------------------------------- /assets/fonts/Roboto-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/falsisdev/mangile/eb3a81544c2ca36d34796041d713fc8b34bd4181/assets/fonts/Roboto-Bold.ttf -------------------------------------------------------------------------------- /assets/fonts/Roboto-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/falsisdev/mangile/eb3a81544c2ca36d34796041d713fc8b34bd4181/assets/fonts/Roboto-BoldItalic.ttf -------------------------------------------------------------------------------- /assets/fonts/Roboto-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/falsisdev/mangile/eb3a81544c2ca36d34796041d713fc8b34bd4181/assets/fonts/Roboto-Italic.ttf -------------------------------------------------------------------------------- /assets/fonts/Roboto-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/falsisdev/mangile/eb3a81544c2ca36d34796041d713fc8b34bd4181/assets/fonts/Roboto-Regular.ttf -------------------------------------------------------------------------------- /assets/images/logo_kana.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/falsisdev/mangile/eb3a81544c2ca36d34796041d713fc8b34bd4181/assets/images/logo_kana.png -------------------------------------------------------------------------------- /assets/images/logo_latin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/falsisdev/mangile/eb3a81544c2ca36d34796041d713fc8b34bd4181/assets/images/logo_latin.png -------------------------------------------------------------------------------- /components/Card/BookcaseCard.vue: -------------------------------------------------------------------------------- 1 | 23 | 85 | 100 | -------------------------------------------------------------------------------- /components/Card/DefaultCard.vue: -------------------------------------------------------------------------------- 1 | 24 | 337 | -------------------------------------------------------------------------------- /components/Card/ListsCard.vue: -------------------------------------------------------------------------------- 1 | 29 | 85 | 100 | -------------------------------------------------------------------------------- /components/Card/PersonMangaCard.vue: -------------------------------------------------------------------------------- 1 | 20 | 78 | 93 | -------------------------------------------------------------------------------- /components/Card/RelationCard.vue: -------------------------------------------------------------------------------- 1 | 22 | 84 | 99 | -------------------------------------------------------------------------------- /components/Layout/Footer.vue: -------------------------------------------------------------------------------- 1 | 15 | -------------------------------------------------------------------------------- /components/Layout/MobileNavbar.vue: -------------------------------------------------------------------------------- 1 | 32 | 361 | -------------------------------------------------------------------------------- /components/Layout/Navbar.vue: -------------------------------------------------------------------------------- 1 | 34 | 391 | -------------------------------------------------------------------------------- /components/Swiper/AutoSwiper.vue: -------------------------------------------------------------------------------- 1 | 7 | 79 | -------------------------------------------------------------------------------- /components/Swiper/FreeSwiper.vue: -------------------------------------------------------------------------------- 1 | 20 | 32 | -------------------------------------------------------------------------------- /components/Swiper/HeroSwiper.vue: -------------------------------------------------------------------------------- 1 | 9 | 141 | -------------------------------------------------------------------------------- /components/Swiper/LastsSwiper.vue: -------------------------------------------------------------------------------- 1 | 20 | 32 | 47 | -------------------------------------------------------------------------------- /components/Swiper/Recommendations.vue: -------------------------------------------------------------------------------- 1 | 22 | 82 | 97 | -------------------------------------------------------------------------------- /content/articles/about.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Hakkımızda" 3 | description: "Mangile Hakkında" 4 | --- 5 | 6 | # Hakkımızda 7 | 8 | Merhaba, ben Falsis! Sitenin yapımcısıyım. Bu sayfada sen, değerli 9 | okuyucuya, bu web sitesinin nasıl kurulduğunu açıklayacağım. Arkana 10 | yaslan. 11 | 12 | # Neden "Mangile"? 13 | 14 | Herkesin merak ettiği sorulardan biri, Mangile ismi nereden geliyor? Neden 15 | bu web sitesinin ismi Mangile? 16 | Bilmem, salladım. 17 | Açıkçası bu web sitesinin yapımına başlamadan önce 18 | Ani-Plus 21 | isimli ufak çaplı, kullanıcıların ücretsiz anime izlemelerini sağlayan bir 22 | proje üzerinde çalışıyordum. Hala çalışıyor olsam muhtemelen iyi yerlere 23 | gelirdi ama, tutmayacağını düşünüp bıraktım. Ardından 24 | MangaDex 25 | isimli yabancı manga sitesinden manga okurken, kendi kendime dedim ki; 26 | "Ben neden bunun gibi türkçe bir site yapmıyorum ki?". Tabi hemen işe 27 | koyulmadım. Benim bu sonuçta nerde hemen işe koyulmak. Önce siteyi yapacak 28 | bir araç bulmam lazımdı. Hiç de zor olmadı tabii... Bu fikir aklıma 29 | geldiğinde 30 | Nuxt henüz 2. 31 | versiyonundaydı ve çok ağır, hantal, yavaş bir framework idi. Ben de bir 32 | süre bekledim ve 1 ay kadar sonra rc (beta) sürümünden stabil sürüme geçiş 33 | yapan Nuxt 3'ü kullanmaya başladım. Biraz alıştıktan sonra hem 34 | Vue 3 hem 35 | Vite 36 | kullanan bu framework'ü Mangile için kullanmaya başladım. Daha sonra bir 37 | UI kütüphanesi kullanmam gerekti. Ben de bir süre 38 | Vuetify 41 | mı kullansam diye düşünürken, Material 3'ün henüz web için 42 | yayınlanmadığını hatırlayıp 43 | TailwindCSS 46 | kullanmaya karar verdim. Tabiki bu yeterli değil. Şimdi bana "Ya kardeşim 47 | nesi yeterli değil, bal gibi şey işte." diyebilirsiniz ama ben çok tembel 48 | biriyimdir. Bana hazır componentler lazım ki kendim uzun uzun kod 49 | yazmaklar uğraşmayayım. Zaman falan kazanırız ya... Bu fikirden sonra da 50 | DaisyUI 51 | kullanmakta karar kıldım.

52 | Şimdi bazılarınızın "Ne anlatıyorsun be adam?! Ben ne anlayayım bu tür 53 | saçmalıklardan! Sadede gel sadede..." dediğine eminim. O zaman devam 54 | edelim: 55 | 59 | 60 | # Peki ya... Ben kimim? 61 | 62 | Kimlik bilgilerimi size sızdıracağımı falan düşünmeyin. Kısaca kendimden 63 | bahsedeyim: Ben Falsis, Falsis Kremlin. Gerçek adım Kerem. İzmir, 64 | Türkiye'de yaşıyorum. Bu yazıyı yazarken (2022 4. Çeyrek) 9. sınıfa giden 65 | bir lise öğrencisinden ibaretim. Yazılıma 5. sınıfta babamın çok 66 | meraklılığıyla başladım. Aslında babam başladı, 1 hafta sonra bıraktı 67 | gerçi ama... Ben devam ettim. Bu siteyi yapana kadar devam ettim. Hala 68 | daha devam ediyorum. Yeni şeyler öğrenmeyi seviyorum, tabiki ilgi çekici 69 | olmak şartıyla. Araştırmak hobimdir, ayrıca boş zamanımda Pes 2017 falan 70 | oynarım. Benimle ilgili daha fazla bilgi istiyorsan seni meraklı okuyucu, 71 | buraya 74 | bakabilirsin. 75 | İşte böyle dostum! Bizimle iletişime geçmek için 76 | iletişim sayfasına 77 | bakabilirsin. Sitenin Kullanıcı Arayüzü (UI) ile ilgili kafan karışıksa, 78 | şuradan sitedeki 79 | işaretlerin ne anlama geldiğine bakabilirsin. İyi okumalar! 80 | 84 | -------------------------------------------------------------------------------- /content/articles/terms.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Kullanıcı Sözleşmesi" 3 | description: "Mangile Kullanıcı Sözleşmesi" 4 | --- 5 | 6 | # Kullanıcı Sözleşmesi 7 | 8 | Sevgili ziyaretçimiz, lütfen web sitemizi ziyaret etmeden önce kullanım koşulları sözleşmesini dikkatlice okuyunuz. Siteye erişiminiz tamamen bu sözleşmeyi kabulünüze ve bu sözleşme ile belirlenen şartlara uymanıza bağlıdır. Şayet bu sözleşmede yazan herhangi bir koşulu kabul etmiyorsanız, lütfen siteye erişiminizi sonlandırınız. Siteye erişiminizi sürdürdüğünüz takdirde, koşulsuz ve kısıtlamasız olarak, sözleşme metninin tamamını kabul ettiğinizin, tarafımızca varsayılacağını lütfen unutmayınız. 9 | 10 | https://mangile.vercel.app web sitesi Mangile Organizasyonu tarafından yönetilmekte olup, bundan sonra Mangile olarak anılacaktır. Bu siteye ilişkin Kullanım Koşulları, yayınlanmakla yürürlüğe girer. Değişiklik yapma hakkı, tek taraflı olarak Mangile'a aittir ve Mangile üzerinden güncel olarak paylaşılacak olan bu değişiklikleri, tüm kullanıcılarımız baştan kabul etmiş sayılır. 11 | 12 | # Gizlilik 13 | 14 | Gizlilik, ayrı bir sayfada, kişisel verilerinizin tarafımızca işlenmesinin esaslarını düzenlemek üzere mevcuttur. Mangile'ı kullandığınız takdirde, bu verilerin işlenmesinin gizlilik politikasına uygun olarak gerçekleştiğini kabul edersiniz. 15 | 16 | # Hizmet Kapsamı 17 | 18 | Mangile Organizasyonu olarak, sunacağımız hizmetlerin kapsamını ve niteliğini, yasalar çerçevesinde belirlemekte tamamen serbest olup; hizmetlere ilişkin yapacağımız değişiklikler, Mangile'da yayınlanmakla yürürlüğe girmiş sayılacaktır. 19 | 20 | # Telif Hakları 21 | 22 | ``` 23 | Apache License 24 | Version 2.0, January 2004 25 | http://www.apache.org/licenses/ 26 | 27 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 28 | 29 | 1. Definitions. 30 | 31 | "License" shall mean the terms and conditions for use, reproduction, 32 | and distribution as defined by Sections 1 through 9 of this document. 33 | 34 | "Licensor" shall mean the copyright owner or entity authorized by 35 | the copyright owner that is granting the License. 36 | 37 | "Legal Entity" shall mean the union of the acting entity and all 38 | other entities that control, are controlled by, or are under common 39 | control with that entity. For the purposes of this definition, 40 | "control" means (i) the power, direct or indirect, to cause the 41 | direction or management of such entity, whether by contract or 42 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 43 | outstanding shares, or (iii) beneficial ownership of such entity. 44 | 45 | "You" (or "Your") shall mean an individual or Legal Entity 46 | exercising permissions granted by this License. 47 | 48 | "Source" form shall mean the preferred form for making modifications, 49 | including but not limited to software source code, documentation 50 | source, and configuration files. 51 | 52 | "Object" form shall mean any form resulting from mechanical 53 | transformation or translation of a Source form, including but 54 | not limited to compiled object code, generated documentation, 55 | and conversions to other media types. 56 | 57 | "Work" shall mean the work of authorship, whether in Source or 58 | Object form, made available under the License, as indicated by a 59 | copyright notice that is included in or attached to the work 60 | (an example is provided in the Appendix below). 61 | 62 | "Derivative Works" shall mean any work, whether in Source or Object 63 | form, that is based on (or derived from) the Work and for which the 64 | editorial revisions, annotations, elaborations, or other modifications 65 | represent, as a whole, an original work of authorship. For the purposes 66 | of this License, Derivative Works shall not include works that remain 67 | separable from, or merely link (or bind by name) to the interfaces of, 68 | the Work and Derivative Works thereof. 69 | 70 | "Contribution" shall mean any work of authorship, including 71 | the original version of the Work and any modifications or additions 72 | to that Work or Derivative Works thereof, that is intentionally 73 | submitted to Licensor for inclusion in the Work by the copyright owner 74 | or by an individual or Legal Entity authorized to submit on behalf of 75 | the copyright owner. For the purposes of this definition, "submitted" 76 | means any form of electronic, verbal, or written communication sent 77 | to the Licensor or its representatives, including but not limited to 78 | communication on electronic mailing lists, source code control systems, 79 | and issue tracking systems that are managed by, or on behalf of, the 80 | Licensor for the purpose of discussing and improving the Work, but 81 | excluding communication that is conspicuously marked or otherwise 82 | designated in writing by the copyright owner as "Not a Contribution." 83 | 84 | "Contributor" shall mean Licensor and any individual or Legal Entity 85 | on behalf of whom a Contribution has been received by Licensor and 86 | subsequently incorporated within the Work. 87 | 88 | 2. Grant of Copyright License. Subject to the terms and conditions of 89 | this License, each Contributor hereby grants to You a perpetual, 90 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 91 | copyright license to reproduce, prepare Derivative Works of, 92 | publicly display, publicly perform, sublicense, and distribute the 93 | Work and such Derivative Works in Source or Object form. 94 | 95 | 3. Grant of Patent License. Subject to the terms and conditions of 96 | this License, each Contributor hereby grants to You a perpetual, 97 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 98 | (except as stated in this section) patent license to make, have made, 99 | use, offer to sell, sell, import, and otherwise transfer the Work, 100 | where such license applies only to those patent claims licensable 101 | by such Contributor that are necessarily infringed by their 102 | Contribution(s) alone or by combination of their Contribution(s) 103 | with the Work to which such Contribution(s) was submitted. If You 104 | institute patent litigation against any entity (including a 105 | cross-claim or counterclaim in a lawsuit) alleging that the Work 106 | or a Contribution incorporated within the Work constitutes direct 107 | or contributory patent infringement, then any patent licenses 108 | granted to You under this License for that Work shall terminate 109 | as of the date such litigation is filed. 110 | 111 | 4. Redistribution. You may reproduce and distribute copies of the 112 | Work or Derivative Works thereof in any medium, with or without 113 | modifications, and in Source or Object form, provided that You 114 | meet the following conditions: 115 | 116 | (a) You must give any other recipients of the Work or 117 | Derivative Works a copy of this License; and 118 | 119 | (b) You must cause any modified files to carry prominent notices 120 | stating that You changed the files; and 121 | 122 | (c) You must retain, in the Source form of any Derivative Works 123 | that You distribute, all copyright, patent, trademark, and 124 | attribution notices from the Source form of the Work, 125 | excluding those notices that do not pertain to any part of 126 | the Derivative Works; and 127 | 128 | (d) If the Work includes a "NOTICE" text file as part of its 129 | distribution, then any Derivative Works that You distribute must 130 | include a readable copy of the attribution notices contained 131 | within such NOTICE file, excluding those notices that do not 132 | pertain to any part of the Derivative Works, in at least one 133 | of the following places: within a NOTICE text file distributed 134 | as part of the Derivative Works; within the Source form or 135 | documentation, if provided along with the Derivative Works; or, 136 | within a display generated by the Derivative Works, if and 137 | wherever such third-party notices normally appear. The contents 138 | of the NOTICE file are for informational purposes only and 139 | do not modify the License. You may add Your own attribution 140 | notices within Derivative Works that You distribute, alongside 141 | or as an addendum to the NOTICE text from the Work, provided 142 | that such additional attribution notices cannot be construed 143 | as modifying the License. 144 | 145 | You may add Your own copyright statement to Your modifications and 146 | may provide additional or different license terms and conditions 147 | for use, reproduction, or distribution of Your modifications, or 148 | for any such Derivative Works as a whole, provided Your use, 149 | reproduction, and distribution of the Work otherwise complies with 150 | the conditions stated in this License. 151 | 152 | 5. Submission of Contributions. Unless You explicitly state otherwise, 153 | any Contribution intentionally submitted for inclusion in the Work 154 | by You to the Licensor shall be under the terms and conditions of 155 | this License, without any additional terms or conditions. 156 | Notwithstanding the above, nothing herein shall supersede or modify 157 | the terms of any separate license agreement you may have executed 158 | with Licensor regarding such Contributions. 159 | 160 | 6. Trademarks. This License does not grant permission to use the trade 161 | names, trademarks, service marks, or product names of the Licensor, 162 | except as required for reasonable and customary use in describing the 163 | origin of the Work and reproducing the content of the NOTICE file. 164 | 165 | 7. Disclaimer of Warranty. Unless required by applicable law or 166 | agreed to in writing, Licensor provides the Work (and each 167 | Contributor provides its Contributions) on an "AS IS" BASIS, 168 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 169 | implied, including, without limitation, any warranties or conditions 170 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 171 | PARTICULAR PURPOSE. You are solely responsible for determining the 172 | appropriateness of using or redistributing the Work and assume any 173 | risks associated with Your exercise of permissions under this License. 174 | 175 | 8. Limitation of Liability. In no event and under no legal theory, 176 | whether in tort (including negligence), contract, or otherwise, 177 | unless required by applicable law (such as deliberate and grossly 178 | negligent acts) or agreed to in writing, shall any Contributor be 179 | liable to You for damages, including any direct, indirect, special, 180 | incidental, or consequential damages of any character arising as a 181 | result of this License or out of the use or inability to use the 182 | Work (including but not limited to damages for loss of goodwill, 183 | work stoppage, computer failure or malfunction, or any and all 184 | other commercial damages or losses), even if such Contributor 185 | has been advised of the possibility of such damages. 186 | 187 | 9. Accepting Warranty or Additional Liability. While redistributing 188 | the Work or Derivative Works thereof, You may choose to offer, 189 | and charge a fee for, acceptance of support, warranty, indemnity, 190 | or other liability obligations and/or rights consistent with this 191 | License. However, in accepting such obligations, You may act only 192 | on Your own behalf and on Your sole responsibility, not on behalf 193 | of any other Contributor, and only if You agree to indemnify, 194 | defend, and hold each Contributor harmless for any liability 195 | incurred by, or claims asserted against, such Contributor by reason 196 | of your accepting any such warranty or additional liability. 197 | 198 | END OF TERMS AND CONDITIONS 199 | 200 | APPENDIX: How to apply the Apache License to your work. 201 | 202 | To apply the Apache License to your work, attach the following 203 | boilerplate notice, with the fields enclosed by brackets "[]" 204 | replaced with your own identifying information. (Don't include 205 | the brackets!) The text should be enclosed in the appropriate 206 | comment syntax for the file format. We also recommend that a 207 | file or class name and description of purpose be included on the 208 | same "printed page" as the copyright notice for easier 209 | identification within third-party archives. 210 | 211 | Copyright 2022 FalsisDev 212 | 213 | Licensed under the Apache License, Version 2.0 (the "License"); 214 | you may not use this file except in compliance with the License. 215 | You may obtain a copy of the License at 216 | 217 | http://www.apache.org/licenses/LICENSE-2.0 218 | 219 | Unless required by applicable law or agreed to in writing, software 220 | distributed under the License is distributed on an "AS IS" BASIS, 221 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 222 | See the License for the specific language governing permissions and 223 | limitations under the License. 224 | ``` 225 | 226 | # Genel Hükümler 227 | 228 | - Kullanıcıların tamamı, Mangile'ı yalnızca hukuka uygun ve şahsi amaçlarla kullanacaklarını ve üçüncü kişinin haklarına tecavüz teşkil edecek nitelikteki herhangi bir faaliyette bulunmayacağını taahhüt eder. Mangile dâhilinde yaptıkları işlem ve eylemlerindeki, hukuki ve cezai sorumlulukları kendilerine aittir. İşbu iş ve eylemler sebebiyle, üçüncü kişilerin uğradıkları veya uğrayabilecekleri zararlardan dolayı Mangile'nin doğrudan ve/veya dolaylı hiçbir sorumluluğu yoktur. 229 | - Mangile'de mevcut bilgilerin doğruluk ve güncelliğini sağlamak için elimizden geleni yapmaktayız. Lakin gösterdiğimiz çabaya rağmen, bu bilgiler, fiili değişikliklerin gerisinde kalabilir, birtakım farklılıklar olabilir. Bu sebeple, site içerisinde yer alan bilgilerin doğruluğu ve güncelliği ile ilgili tarafımızca, açık veya zımni, herhangi bir garanti verilmemekte, hiçbir taahhütte bulunulmamaktadır. 230 | - Mangile'de üçüncü şahıslar tarafından işletilen ve içerikleri tarafımızca bilinmeyen diğer web sitelerine, uygulamalara ve platformlara köprüler (hyperlink) bulunabilir. Mangile, işlevsellik yalnızca bu sitelere ulaşımı sağlamakta olup, içerikleri ile ilgili hiçbir sorumluluk kabul etmemekteyiz. 231 | - Mangile'yi virüslerden temizlenmiş tutmak konusunda elimizden geleni yapsak da, virüslerin tamamen bulunmadığı garantisini vermemekteyiz. Bu nedenle veri indirirken, virüslere karşı gerekli önlemi almak, kullanıcıların sorumluluğundadır. Virüs vb. kötü amaçlı programlar, kodlar veya materyallerin sebep olabileceği zararlardan dolayı sorumluluk kabul etmemekteyiz. 232 | - Mangile'de sunulan hizmetlerde, kusur veya hata olmayacağına ya da kesintisiz hizmet verileceğine dair garanti vermemekteyiz. Mangile'ye ve sitenin hizmetlerine veya herhangi bir bölümüne olan erişiminizi önceden bildirmeksizin herhangi bir zamanda sonlandırabiliriz. 233 | 234 | # Sorumluluğun Sınırlandırılması 235 | 236 | Mangile'nin kullanımından doğan zararlara ilişkin sorumluluğumuz, kast ve ağır ihmal ile sınırlıdır. Sözleşmenin ihlalinden doğan zararlarda, talep edilebilecek toplam tazminat, öngörülebilir hasarlar ile sınırlıdır. Yukarıda bahsedilen sorumluluk sınırlamaları aynı zamanda insan hayatına, bedeni yaralanmaya veya bir kişinin sağlığına gelebilecek zararlar durumunda geçerli değildir. Hukuken mücbir sebep sayılan tüm durumlarda, gecikme, ifa etmeme veya temerrütten dolayı, herhangi bir tazminat yükümlülüğümüz doğmayacaktır. 237 | 238 | Uyuşmazlık Çözümü: Mangile Kullanıcı Sözleşmesi'nin uygulanmasından veya yorumlanmasından doğacak her türlü uyuşmazlığın çözümünde, Türkiye Cumhuriyeti yasaları uygulanır; Yozgat Adliyesi Mahkemeleri ve İcra Daireleri yetkilidir. 239 | 240 | Bu kullanım koşulları, https://sartlar.com aracılığıyla oluşturulmuştur. 241 | -------------------------------------------------------------------------------- /errorx.vue: -------------------------------------------------------------------------------- 1 | 15 | 42 | -------------------------------------------------------------------------------- /layouts/default.vue: -------------------------------------------------------------------------------- 1 | 12 | -------------------------------------------------------------------------------- /layouts/mobile.vue: -------------------------------------------------------------------------------- 1 | 9 | -------------------------------------------------------------------------------- /nuxt.config.ts: -------------------------------------------------------------------------------- 1 | // https://nuxt.com/docs/api/configuration/nuxt-config 2 | import tailwindcss from "@tailwindcss/vite"; 3 | export default defineNuxtConfig({ 4 | future: { 5 | compatibilityVersion: 4, 6 | }, 7 | modules: [ 8 | "@vueuse/nuxt", 9 | "nuxt-swiper", 10 | "@nuxt/icon", 11 | "@nuxtjs/device", 12 | "@logto/nuxt", 13 | "@nuxtjs/sanity", 14 | "@nuxt/content", 15 | '@nuxt/image', 16 | "nuxt-disqus", 17 | ], 18 | sanity: { 19 | projectId: process.env.NUXT_SANITY_PROJECT_ID, 20 | }, 21 | disqus: { 22 | shortname: "mangile", 23 | }, 24 | runtimeConfig: { 25 | logto: { 26 | endpoint: "", 27 | appId: "", 28 | appSecret: "", 29 | cookieEncryptionKey: "", 30 | }, 31 | sanity: { 32 | token: process.env.NUXT_SANITY_TOKEN, 33 | }, 34 | public: { 35 | subjectToken: process.env.NUXT_SUBJECT_TOKEN, 36 | m2mAppID: process.env.NUXT_M2M_APP_ID, 37 | m2mAppSecret: process.env.NUXT_M2M_APP_SECRET, 38 | disqusShortname: "mangile", 39 | }, 40 | }, 41 | compatibilityDate: "2024-04-03", 42 | devtools: { enabled: true }, 43 | css: ["~/assets/css/main.css"], 44 | app: { 45 | pageTransition: { name: "page", mode: "out-in" }, 46 | }, 47 | components: [ 48 | { 49 | path: "~/components", 50 | pathPrefix: false, 51 | }, 52 | ], 53 | vite: { 54 | plugins: [ 55 | tailwindcss(), 56 | ], 57 | }, 58 | }); 59 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mangile", 3 | "private": true, 4 | "type": "module", 5 | "scripts": { 6 | "build": "nuxt build", 7 | "dev": "nuxt dev", 8 | "generate": "nuxt generate", 9 | "preview": "nuxt preview", 10 | "postinstall": "nuxt prepare" 11 | }, 12 | "dependencies": { 13 | "@iconify-json/flag": "^1.2.5", 14 | "@iconify-json/material-symbols": "^1.2.17", 15 | "@iconify-json/mdi": "^1.2.3", 16 | "@iconify-json/mingcute": "^1.2.3", 17 | "@iconify-json/simple-icons": "^1.2.29", 18 | "@logto/nuxt": "^0.3.4", 19 | "@nuxt/content": "^2.13.4", 20 | "@nuxt/icon": "^1.11.0", 21 | "@nuxt/image": "1.9.0", 22 | "@nuxtjs/device": "^3.2.4", 23 | "@nuxtjs/sanity": "^1.13.3", 24 | "@sanity/client": "^6.28.3", 25 | "@sanity/image-url": "^1.1.0", 26 | "@tailwindcss/vite": "^4.0.17", 27 | "@vueuse/core": "^11.3.0", 28 | "@vueuse/nuxt": "^11.3.0", 29 | "nuxt": "^3.16.1", 30 | "nuxt-disqus": "1.1.1", 31 | "nuxt-swiper": "^1.2.2", 32 | "sanity": "^3.81.0", 33 | "tailwindcss": "^4.0.17", 34 | "vue": "latest" 35 | }, 36 | "packageManager": "pnpm@9.7.0+sha512.dc09430156b427f5ecfc79888899e1c39d2d690f004be70e05230b72cb173d96839587545d09429b55ac3c429c801b4dc3c0e002f653830a420fa2dd4e3cf9cf", 37 | "devDependencies": { 38 | "@tailwindcss/typography": "^0.5.16", 39 | "autoprefixer": "^10.4.21", 40 | "daisyui": "^5.0.9", 41 | "postcss": "^8.5.3" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /pages/[...slug].vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /pages/index.vue: -------------------------------------------------------------------------------- 1 | 201 | 261 | 276 | -------------------------------------------------------------------------------- /pages/person/[personID].vue: -------------------------------------------------------------------------------- 1 | 12 | 74 | -------------------------------------------------------------------------------- /pages/scan/[scanID]/index.vue: -------------------------------------------------------------------------------- 1 | 54 | 55 | -------------------------------------------------------------------------------- /pages/search.vue: -------------------------------------------------------------------------------- 1 | 101 | 102 | 305 | -------------------------------------------------------------------------------- /pages/title/[titleID]/read/[key].vue: -------------------------------------------------------------------------------- 1 | 185 | 364 | -------------------------------------------------------------------------------- /pages/user/[userID]/index.vue: -------------------------------------------------------------------------------- 1 | 46 | 234 | -------------------------------------------------------------------------------- /pages/user/[userID]/library.vue: -------------------------------------------------------------------------------- 1 | 68 | 265 | -------------------------------------------------------------------------------- /pages/user/[userID]/list/[listID].vue: -------------------------------------------------------------------------------- 1 | 82 | 83 | 197 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/falsisdev/mangile/eb3a81544c2ca36d34796041d713fc8b34bd4181/public/favicon.ico -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /server/api/index.ts: -------------------------------------------------------------------------------- 1 | export default defineEventHandler(async (event) => { 2 | return { 3 | response: "Çalışıyor.", 4 | }; 5 | }); 6 | -------------------------------------------------------------------------------- /server/api/title/[titleID]/index.ts: -------------------------------------------------------------------------------- 1 | import { defineEventHandler } from "h3"; 2 | import { useSanity } from "#imports"; 3 | import imageUrlBuilder from "@sanity/image-url"; 4 | 5 | interface Chapter { 6 | chapterNumber: number; 7 | source: string; 8 | } 9 | 10 | interface Manga { 11 | relations?: { relation: string; entry: { mal_id: number; type: string }[] }[]; 12 | images?: { jpg: { large_image_url: string } }; 13 | } 14 | 15 | interface JikanResponse { 16 | data: T; 17 | } 18 | 19 | export default defineEventHandler(async (event) => { 20 | const { titleID } = event.context.params as { titleID?: string }; 21 | if (!titleID) { 22 | return { response: "error", message: "titleID is required" }; 23 | } 24 | 25 | try { 26 | const sanity: any = useSanity(); 27 | const builder = imageUrlBuilder(sanity.config); 28 | const getSanityImageUrl = (source: any) => builder.image(source).url(); 29 | const query = `*[_type == "manga" && myAnimeListId == $titleID][0]`; 30 | const sanityData = await sanity.fetch(query, { titleID: Number(titleID) }); 31 | 32 | const sanityDataWithImages = sanityData?.chapters 33 | ? { 34 | ...sanityData, 35 | chapters: sanityData.chapters.map((chapter: any) => ({ 36 | ...chapter, 37 | pages: chapter.pages 38 | ? chapter.pages.map((page: any) => getSanityImageUrl(page)) 39 | : [], 40 | })), 41 | } 42 | : sanityData; 43 | 44 | let groupedChapters: Chapter[][] = []; 45 | let unGroupedChapters: Chapter[] = []; 46 | let scans: string[] = []; 47 | 48 | if (sanityData?.chapters) { 49 | unGroupedChapters = sanityData.chapters; 50 | groupedChapters = Object.values( 51 | unGroupedChapters.reduce>((acc, chapter) => { 52 | acc[chapter.chapterNumber] = acc[chapter.chapterNumber] || []; 53 | acc[chapter.chapterNumber].push(chapter); 54 | return acc; 55 | }, {}) 56 | ); 57 | scans = unGroupedChapters.map((chap) => chap.source); 58 | } 59 | 60 | // Jikan API istekleri 61 | const [mangaRes, imagesRes, recommendationsRes] = await Promise.all([ 62 | fetch(`https://api.jikan.moe/v4/manga/${titleID}/full`).then((res) => 63 | res.json() 64 | ) as Promise>, 65 | fetch(`https://api.jikan.moe/v4/manga/${titleID}/pictures`).then((res) => 66 | res.json() 67 | ) as Promise>, 68 | fetch(`https://api.jikan.moe/v4/manga/${titleID}/recommendations`).then( 69 | (res) => res.json() 70 | ) as Promise>, 71 | ]); 72 | 73 | const manga: Manga = mangaRes.data || {}; 74 | const images = imagesRes.data || []; 75 | const recommendations = recommendationsRes.data || []; 76 | 77 | // İlişkili mangaları çekme 78 | let relations: { relation: string; entry: Manga }[] = []; 79 | if (manga.relations) { 80 | relations = await Promise.all( 81 | manga.relations.flatMap((relation) => 82 | relation.entry 83 | .filter((entry) => entry.type === "manga") 84 | .map(async (entry) => { 85 | const entryData = (await fetch( 86 | `https://api.jikan.moe/v4/manga/${entry.mal_id}/full` 87 | ).then((res) => res.json())) as JikanResponse; 88 | return { relation: relation.relation, entry: entryData.data }; 89 | }) 90 | ) 91 | ); 92 | } 93 | 94 | return { 95 | response: "ok.", 96 | sanityData: sanityDataWithImages, 97 | groupedChapters, 98 | unGroupedChapters, 99 | scans, 100 | manga, 101 | images, 102 | recommendations, 103 | relations, 104 | }; 105 | } catch (error) { 106 | return { response: "error", message: (error as Error).message }; 107 | } 108 | }); 109 | -------------------------------------------------------------------------------- /server/api/title/[titleID]/read/[key].ts: -------------------------------------------------------------------------------- 1 | import { defineEventHandler } from "h3"; 2 | import { useSanity } from "#imports"; 3 | import imageUrlBuilder from "@sanity/image-url"; 4 | 5 | interface Chapter { 6 | chapterNumber: number; 7 | source: string; 8 | pages: { image: string }[]; 9 | } 10 | 11 | export default defineEventHandler(async (event: any) => { 12 | const { titleID } = event.context.params as { titleID?: string }; 13 | if (!titleID) { 14 | return { response: "error", message: "titleID is required" }; 15 | } 16 | 17 | try { 18 | const sanity: any = useSanity(); 19 | const builder = imageUrlBuilder(sanity.config); 20 | const getSanityImageUrl = (source: any) => builder.image(source).url(); 21 | const query = `*[_type == "manga" && myAnimeListId == $titleID][0]`; 22 | const preSanityData = await sanity.fetch(query, { 23 | titleID: Number(titleID), 24 | }); 25 | 26 | if (!preSanityData) { 27 | return { 28 | response: "error", 29 | message: "No data found for the given titleID", 30 | }; 31 | } 32 | 33 | const chapter = preSanityData.chapters.find( 34 | (x: any) => x._key === event.context.params.key 35 | ); 36 | 37 | if (!chapter) { 38 | return { response: "error", message: "Chapter not found" }; 39 | } 40 | 41 | const updatedPages = chapter.pages.map((page: any) => ({ 42 | image: getSanityImageUrl(page.asset._ref), 43 | })); 44 | 45 | return { 46 | response: "ok.", 47 | chapter: { 48 | ...chapter, 49 | pages: updatedPages, 50 | }, 51 | }; 52 | } catch (error) { 53 | return { response: "error", message: (error as Error).message }; 54 | } 55 | }); 56 | -------------------------------------------------------------------------------- /server/api/users/[userID].ts: -------------------------------------------------------------------------------- 1 | export default defineEventHandler(async (event) => { 2 | const config = useRuntimeConfig(event); 3 | const userID = getRouterParam(event, "userID"); 4 | const query = getQuery(event); 5 | 6 | if (query.appSecret == config.public.m2mAppSecret) { 7 | const accessTokenData = await fetch(`${config.logto.endpoint}/oidc/token`, { 8 | method: "POST", 9 | headers: { 10 | "Content-Type": "application/x-www-form-urlencoded", 11 | Authorization: `Basic ${Buffer.from( 12 | `${config.public.m2mAppID}:${config.public.m2mAppSecret}` 13 | ).toString("base64")}`, 14 | }, 15 | body: new URLSearchParams({ 16 | grant_type: "client_credentials", 17 | resource: `${config.logto.endpoint}/api`, 18 | scope: "all", 19 | }).toString(), 20 | }).then((response) => response.json()); 21 | 22 | const accessToken = accessTokenData.access_token; 23 | 24 | const data = await fetch(`${config.logto.endpoint}/api/users/${userID}`, { 25 | method: "GET", 26 | headers: { 27 | Authorization: `Bearer ${accessToken}`, 28 | }, 29 | }).then((response) => response.json()); 30 | 31 | if (event.method == "GET") { 32 | return data; 33 | } else if (event.method == "PATCH") { 34 | const body = await readBody(event).catch(() => {}); 35 | const resp = await fetch(`${config.logto.endpoint}/api/users/${userID}`, { 36 | method: "PATCH", 37 | headers: { 38 | Authorization: `Bearer ${accessToken}`, 39 | "content-type": "application/json", 40 | }, 41 | body: JSON.stringify(body), 42 | }); 43 | } 44 | } else { 45 | setResponseStatus(event, 401, "Yetkisiz."); 46 | return { response: "Yetkisiz." }; 47 | } 48 | }); 49 | -------------------------------------------------------------------------------- /server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../.nuxt/tsconfig.server.json" 3 | } 4 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // https://nuxt.com/docs/guide/concepts/typescript 3 | "extends": "./.nuxt/tsconfig.json" 4 | } 5 | --------------------------------------------------------------------------------