Database Setup
The Scanlation Template uses MongoDB as its database. This guide covers local setup with Docker and production options.
Local Development (Docker)
The easiest way to run MongoDB locally is using the included Docker Compose setup:
pnpm docker:up# or: make upThis starts MongoDB on port 27017. The default connection string is:
mongodb://localhost:27017/scanlationProduction Options
Setting Up MongoDB Atlas
- 1Create an account at mongodb.com/atlas
- 2Create a new cluster (M0 free tier works for small projects)
- 3Set up a database user with read/write access
- 4Add your IP address (or 0.0.0.0/0 for all IPs) to the IP Access List
- 5Get your connection string from "Connect" > "Connect your application"
- 6Replace <password> with your database user password
mongodb+srv://username:password@cluster0.xxxxx.mongodb.net/scanlation?retryWrites=true&w=majorityData Management
Backup Reminder: Always set up regular backups for your production database. MongoDB Atlas includes automated backups.
Useful Commands
mongodump --uri="mongodb://localhost:27017/scanlation" --out=./backupmongorestore --uri="mongodb://localhost:27017/scanlation" ./backup/scanlationDatabase Collections
The template uses the following collections (auto-created):
seriesManga/manhwa series metadata with i18n support
chaptersIndividual chapter data and monetization settings
usersUser accounts, premium status, and tokens
bookmarksUser reading progress and notifications
ratingsUser ratings for series (1-5 stars)
commentsUser comments on series and chapters
Collection Schemas
Below are the actual schema definitions used by the template. These are auto-created when data is first inserted.
Series Collection
{
// Basic Info (with i18n support)
title: {
en: String (required),
es?: String,
pt?: String,
ja?: String,
zh?: String,
ko?: String
},
slug: String (unique, lowercase),
description: {
en: String (required),
es?: String,
pt?: String,
...
},
// Media
coverImage: String (required),
bannerImage?: String,
// Metadata
author: String (required),
artist?: String,
genres: [String],
tags: [String],
// Status & Type
status: 'ongoing' | 'completed' | 'hiatus' | 'cancelled',
type: 'manga' | 'manhwa' | 'manhua' | 'webtoon' | 'comic',
contentRating: 'safe' | 'suggestive' | 'mature' | 'explicit',
// Stats (auto-updated)
viewCount: Number,
bookmarkCount: Number,
chapterCount: Number,
averageRating: Number (0-5),
ratingCount: Number,
// Publishing
isPublished: Boolean,
isFeatured: Boolean,
// Timestamps
createdAt: Date,
updatedAt: Date
}Chapters Collection
{
series: ObjectId (ref: 'Series'),
// Chapter Info
number: Number (required),
volume?: Number,
title?: String,
// Images (paths to cloud storage)
images: [String] (required, min: 1),
pageCount: Number (required),
// Monetization
isPremium: Boolean (default: false),
tokenCost: Number (default: 0),
freeAfterDays: Number (default: 0),
publishedAt: Date,
// Stats
viewCount: Number,
// Publishing
isPublished: Boolean (default: true),
// Virtual: isFreeNow (computed from freeAfterDays + publishedAt)
// Timestamps
createdAt: Date,
updatedAt: Date
}Users Collection
{
// Auth (from OAuth provider)
email: String (unique, required),
name: String (required),
image?: String,
provider: 'google' | 'discord' | 'twitter',
providerId: String,
// Profile
username?: String (unique, 3-20 chars),
bio?: String (max: 500),
// Flags
isAdmin: Boolean,
isModerator: Boolean,
isBanned: Boolean,
// Premium/Subscription
isPremium: Boolean,
premiumExpiresAt?: Date,
stripeCustomerId?: String,
stripeSubscriptionId?: String,
// Token System
tokenBalance: Number (default: 0),
// 2FA
twoFactorEnabled: Boolean,
twoFactorSecret?: String,
backupCodes?: [String],
// Stats
chaptersRead: Number,
level: Number,
experience: Number,
// Timestamps
createdAt: Date,
updatedAt: Date,
lastLoginAt?: Date
}Bookmarks Collection
{
user: ObjectId (ref: 'User'),
series: ObjectId (ref: 'Series'),
// Reading Progress
lastReadChapter?: ObjectId (ref: 'Chapter'),
lastReadChapterNumber?: Number,
lastReadAt?: Date,
// Notifications
notifyOnUpdate: Boolean (default: true),
// Timestamps
createdAt: Date,
updatedAt: Date
}
// Unique index: { user, series }Comments Collection
{
user: ObjectId (ref: 'User'),
series?: ObjectId (ref: 'Series'),
chapter?: ObjectId (ref: 'Chapter'),
parentComment?: ObjectId (ref: 'Comment'),
// Content
content: String (required, 1-2000 chars),
// Reactions
likes: [ObjectId] (refs: 'User'),
dislikes: [ObjectId] (refs: 'User'),
likeCount: Number,
dislikeCount: Number,
// Status
isEdited: Boolean,
isDeleted: Boolean,
isReported: Boolean,
// Reply tracking
replyCount: Number,
// Timestamps
createdAt: Date,
updatedAt: Date
}Ratings Collection
{
user: ObjectId (ref: 'User'),
series: ObjectId (ref: 'Series'),
// Rating (1-5 stars)
rating: Number (required, min: 1, max: 5),
// Timestamps
createdAt: Date,
updatedAt: Date
}
// Unique index: { user, series }