यह पेज मशीन से अनुवादित किया गया था। कोई गलती दिखी?इसे बेहतर बनाने में मदद करें।
Skip to content

डेटाबेस

SnapOtter डेटा स्थायित्व के लिए Drizzle ORM (pg-core / node-postgres) के साथ PostgreSQL 17 का उपयोग करता है। स्कीमा apps/api/src/db/schema.ts में परिभाषित है।

कनेक्शन DATABASE_URL एनवायरनमेंट वेरिएबल के माध्यम से कॉन्फ़िगर किया जाता है (डिफ़ॉल्ट postgres://snapotter:snapotter@postgres:5432/snapotter)। Docker Compose में, Postgres कंटेनर अपना डेटा SnapOtter-pgdata नामित वॉल्यूम में संग्रहीत करता है। अनुरोध एक ऐसी भूमिका पर सर्व किए जाते हैं जो केवल पंक्तियाँ पढ़ और लिख सकती है, जिसे नीचे न्यूनतम-विशेषाधिकार भूमिकाएँ में शामिल किया गया है।

टेबल

users

उपयोगकर्ता खातों को संग्रहीत करता है। DEFAULT_USERNAME और DEFAULT_PASSWORD से पहले रन पर स्वचालित रूप से बनाया जाता है।

कॉलमप्रकारनोट्स
iduuidप्राथमिक कुंजी
usernamevarcharअद्वितीय, आवश्यक
passwordHashvarcharscrypt हैश
rolevarcharadmin, editor, या user
mustChangePasswordbooleanअनिवार्य पासवर्ड रीसेट फ़्लैग
createdAttimestampबनाने का समय
updatedAttimestampअंतिम अपडेट समय

sessions

सक्रिय लॉगिन सत्र। प्रत्येक पंक्ति एक सत्र टोकन को एक उपयोगकर्ता से जोड़ती है।

कॉलमप्रकारनोट्स
idvarcharप्राथमिक कुंजी (सत्र टोकन)
userIduuidusers.id के लिए फ़ॉरेन कुंजी
expiresAttimestampसमाप्ति समय
createdAttimestampबनाने का समय

teams

उपयोगकर्ताओं को व्यवस्थित करने के लिए समूह। एडमिन उपयोगकर्ताओं को टीमों में असाइन कर सकते हैं।

कॉलमप्रकारविवरण
iduuidप्राथमिक कुंजी
namevarchar (अद्वितीय, अधिकतम 50 वर्ण)टीम का नाम
createdAttimestampबनाने का समय

api_keys

प्रोग्रामेटिक एक्सेस के लिए API कुंजियाँ। कच्ची कुंजी निर्माण पर एक बार दिखाई जाती है; केवल हैश संग्रहीत किया जाता है।

कॉलमप्रकारनोट्स
iduuidप्राथमिक कुंजी
userIduuidusers.id के लिए फ़ॉरेन कुंजी
keyHashvarcharकुंजी का scrypt हैश
namevarcharउपयोगकर्ता द्वारा दिया गया लेबल
createdAttimestampबनाने का समय
lastUsedAttimestampप्रत्येक प्रमाणित अनुरोध पर अपडेट किया जाता है

कुंजियाँ si_ से उपसर्गित होती हैं जिसके बाद 96 हेक्स वर्ण होते हैं (48 रैंडम बाइट्स)।

pipelines

सहेजे गए टूल चेन जिन्हें उपयोगकर्ता UI में बनाते हैं।

कॉलमप्रकारनोट्स
iduuidप्राथमिक कुंजी
namevarcharपाइपलाइन का नाम
descriptionvarcharवैकल्पिक विवरण
stepsjsonb{ toolId, settings } ऑब्जेक्ट का ऐरे
createdAttimestampबनाने का समय

user_files

स्थायी फ़ाइल लाइब्रेरी। सहेजा गया संपादन डिफ़ॉल्ट रूप से एक स्वतंत्र रूट पंक्ति के रूप में डाला जाता है ("नई के रूप में सहेजें": version 1, parentId null, ताकि मूल सूचीबद्ध रहे), या जब आप मूल को अधिलेखित करते हैं तो एक पैरेंट-लिंक्ड संस्करण के रूप में (parentId सेट, version बढ़ा हुआ, उसे प्रतिस्थापित करते हुए)। toolChain कॉलम लागू किए गए टूल को रिकॉर्ड करता है।

कॉलमप्रकारविवरण
iduuidप्राथमिक कुंजी
userIduuidusers के लिए FK (CASCADE DELETE)
originalNamevarcharमूल अपलोड फ़ाइलनाम
storedNamevarcharडिस्क पर फ़ाइलनाम
mimeTypevarcharMIME प्रकार
sizeintegerबाइट्स में फ़ाइल का आकार
widthintegerpx में छवि की चौड़ाई
heightintegerpx में छवि की ऊँचाई
versionintegerसंस्करण संख्या (1 = मूल)
parentIduuid या nulluser_files के लिए FK (पैरेंट संस्करण)
toolChainjsonbइस संस्करण को बनाने के लिए क्रम में लागू किए गए टूल ID
createdAttimestampबनाने का समय

jobs

प्रगति रिपोर्टिंग और सफ़ाई के लिए प्रोसेसिंग जॉब को ट्रैक करता है।

कॉलमप्रकारनोट्स
iduuidप्राथमिक कुंजी
typevarcharटूल या पाइपलाइन पहचानकर्ता
statusvarcharqueued, processing, completed, या failed
progressreal0.0-1.0 अंश
inputFilesjsonbइनपुट फ़ाइल पथों का ऐरे
outputPathvarcharपरिणाम फ़ाइल का पथ
settingsjsonbउपयोग की गई टूल सेटिंग्स
errorvarcharविफल होने पर त्रुटि संदेश
createdAttimestampबनाने का समय
completedAttimestampपूर्ण होने का समय

settings

सर्वर-व्यापी सेटिंग्स के लिए की-वैल्यू स्टोर जिन्हें एडमिन UI से बदल सकते हैं।

कॉलमप्रकारनोट्स
keyvarcharप्राथमिक कुंजी
valuevarcharसेटिंग मान
updatedAttimestampअंतिम अपडेट समय

roles

सूक्ष्म अनुमतियों वाली कस्टम भूमिकाएँ।

कॉलमप्रकारनोट्स
iduuidप्राथमिक कुंजी
namevarcharअद्वितीय भूमिका नाम
descriptionvarcharवैकल्पिक विवरण
permissionsjsonbअनुमति स्ट्रिंग्स का ऐरे
createdAttimestampबनाने का समय

audit_log

सुरक्षा-प्रासंगिक क्रिया लॉग।

कॉलमप्रकारनोट्स
iduuidप्राथमिक कुंजी
userIduuidusers के लिए FK
actionvarcharक्रिया प्रकार
detailsjsonbक्रिया-विशिष्ट डेटा
createdAttimestampक्रिया का समय

user_preferences

प्रति-उपयोगकर्ता UI स्थिति, प्राथमिकता के नाम से कुंजीबद्ध। होम पेज पर पिन किए गए टूल यहीं रखे जाते हैं, जिन्हें PUT /api/v1/preferences के ज़रिए लिखा जाता है।

कॉलमप्रकारनोट्स
userIdtextusers के लिए FK, हटाने पर कैस्केड। key के साथ मिलकर प्राथमिक कुंजी
keytextप्राथमिकता का नाम। userId के साथ मिलकर प्राथमिक कुंजी
valuejsonbप्राथमिकता का डेटा
updatedAttimestampअंतिम लेखन समय

माइग्रेशन

Drizzle स्कीमा माइग्रेशन संभालता है। माइग्रेशन फ़ाइलें apps/api/drizzle/ में रहती हैं। विकास के दौरान:

bash
cd apps/api
npx drizzle-kit generate   # generate a migration from schema changes
npx drizzle-kit migrate    # apply pending migrations

प्रोडक्शन में, लंबित माइग्रेशन स्टार्टअप पर स्वचालित रूप से लागू किए जाते हैं।

न्यूनतम-विशेषाधिकार भूमिकाएँ

दो भूमिकाएँ, दो काम। DATABASE_URL अनुरोधों को सर्व करता है और ऐप की टेबल पर SELECT, INSERT, UPDATE, DELETE तथा उनके सीक्वेंस पर USAGE और SELECT रखता है। पूरी सूची बस इतनी ही है। यह न तो कोई टेबल बना या हटा सकता है, न कोई एक्सटेंशन इंस्टॉल कर सकता है, न TRUNCATE कर सकता है, न pg_authid पढ़ सकता है, न कोई डेटाबेस बना सकता है, न किसी भूमिका को बदल सकता है, और न ही उस drizzle स्कीमा को छू सकता है जहाँ माइग्रेशन इतिहास रहता है।

DATABASE_MIGRATION_URL विशेषाधिकार-प्राप्त वाला है। यह बूट के दौरान माइग्रेशन चलाता है और रनटाइम भूमिका को अनुदान देता है, फिर एक भी अनुरोध सर्व होने से पहले बंद हो जाता है।

Compose और ऑल-इन-वन इमेज पहले से ही इसी तरह जुड़े हुए हैं, मौजूदा इंस्टॉल भी इसमें शामिल हैं। बूट पर SnapOtter रनटाइम भूमिका न होने पर उसे बनाता है, उसे अनुदान देता है, माइग्रेट करता है, फिर पहले से मौजूद टेबल पर भी वही अनुदान लागू कर देता है। अपग्रेड करने के लिए किसी मैनुअल SQL की ज़रूरत नहीं है।

DATABASE_MIGRATION_URL को खाली छोड़ने पर सब कुछ एकल-भूमिका में चलता है, जहाँ DATABASE_URL दोनों काम ठीक वैसे ही करता है जैसे विभाजन से पहले करता था। यह एक समर्थित कॉन्फ़िगरेशन है, अप्रचलित नहीं। मैनेज्ड Postgres पर यही सही जवाब है, जहाँ भूमिकाएँ बनाना अक्सर आपके हाथ में नहीं होता।

बाहरी और मैनेज्ड Postgres

RDS, Supabase, Cloud SQL, या किसी भी ऐसे क्लस्टर पर जिसे आप खुद चलाते हैं, यह विभाजन वैकल्पिक है। रनटाइम भूमिका एक बार बनाएँ:

sql
CREATE ROLE snapotter_app LOGIN PASSWORD 'choose-a-strong-password'
  NOSUPERUSER NOCREATEDB NOCREATEROLE NOBYPASSRLS;

फिर SnapOtter को दोनों कनेक्शन स्ट्रिंग दें, जो एक ही होस्ट, पोर्ट और डेटाबेस की ओर इंगित हों:

bash
DATABASE_URL=postgres://snapotter_app:[email protected]:5432/snapotter
DATABASE_MIGRATION_URL=postgres://snapotter:[email protected]:5432/snapotter

बस इतना ही करें। SnapOtter अनुदान खुद लागू करता है और हर माइग्रेशन के बाद उन्हें दोबारा लागू करता है, इसलिए किसी भविष्य की रिलीज़ में जोड़ी गई टेबल भी बिना किसी के SQL चलाए कवर हो जाती है।

DATABASE_MIGRATION_URL में दी गई भूमिका के पास SnapOtter की टेबल का स्वामित्व होना चाहिए, क्योंकि किसी टेबल पर अनुदान केवल उसका स्वामी ही दे सकता है। मौजूदा इंस्टॉल पर इसका मतलब वही भूमिका है जिससे आप अब तक SnapOtter चलाते आए हैं, न कि इसी काम के लिए नई बनाई गई कोई भूमिका। इसे ऐसी नई भूमिका की ओर इंगित करें जिसके पास कुछ भी नहीं है, तो बूट ठीक यही बात कहती हुई त्रुटि के साथ विफल हो जाता है। रनटाइम भूमिका बनाने और उसे बनाए रखने के लिए इसे CREATEROLE भी चाहिए, और drizzle स्कीमा बनाने का अधिकार भी।

दोनों URL में एक ही भूमिका का नाम दें तो विभाजन बंद हो जाता है, और SnapOtter इसे छिपाने के बजाय लॉग में यह बात बता देता है। अगर आपका प्रदाता आपको ऐसी कोई भूमिका नहीं देता जो टेबल की स्वामी भी हो और CREATEROLE भी रखती हो, तो एकल-भूमिका में चलाएँ।

सुपरयूज़र बिट को क्यों नहीं छेड़ा जाता

SnapOtter किसी भूमिका से SUPERUSER खुद कभी नहीं हटाता। विभाजन से पहले बनाए गए इंस्टॉल पर snapotter ही क्लस्टर का एकमात्र सुपरयूज़र होता है, और उसे नीचे उतारने पर क्लस्टर के पास एक भी सुपरयूज़र नहीं बचेगा, जिसकी भरपाई केवल सर्वर बंद करके सिंगल-यूज़र मोड से ही हो सकती है। इसके बजाय सुरक्षा इस बात से मिलती है कि लंबे समय तक चलने वाला कनेक्शन प्रतिबंधित भूमिका पर ले जाया गया है। सुपरयूज़र बूट के चंद सेकंड के लिए ही तार पर रहता है और फिर चला जाता है।

नए ऑल-इन-वन इंस्टॉल में यह समस्या कभी नहीं आती। उन्हें तीन भूमिकाएँ मिलती हैं: postgres (बूटस्ट्रैप सुपरयूज़र, SnapOtter द्वारा उपयोग की जाने वाली हर कनेक्शन स्ट्रिंग से अनुपस्थित), snapotter (NOSUPERUSER, डेटा का स्वामी, केवल बूट पर कनेक्ट होता है), और snapotter_app (केवल पंक्तियाँ, अनुरोध सर्व करता है)।

किसी पुराने snapotter को फिर भी नीचे उतारना हो, तो पहले एक दूसरा सुपरयूज़र बनाएँ और उससे लॉग इन करके पुष्टि करें कि वह काम करता है। इसके बाद ALTER ROLE snapotter NOSUPERUSER

बैकअप लें और {#backup-and-restore} को पुनर्स्थापित करें

रिलेशनल डेटाबेस पोस्टग्रेज कंटेनर के SnapOtter-pgdata वॉल्यूम में रहता है, ऐप के /data वॉल्यूम में नहीं।

सत्यापन के साथ तार्किक बैकअप (अनुशंसित)

bash
# Dump into PostgreSQL's portable custom archive format
docker exec SnapOtter-postgres \
  pg_dump --format=custom --no-owner -U snapotter snapotter > snapotter.dump
test -s snapotter.dump
docker exec -i SnapOtter-postgres pg_restore --list < snapotter.dump >/dev/null

# Restore into a fresh/disposable target first and fail on the first SQL error
docker exec -i SnapOtter-postgres \
  pg_restore --exit-on-error --clean --if-exists --no-owner \
  -U snapotter -d snapotter < snapotter.dump

दोनों कमांड स्वामी snapotter के रूप में कनेक्ट होते हैं, और उन्हें ऐसा ही करते रहना चाहिए। रनटाइम भूमिका drizzle स्कीमा को नहीं देख सकती, इसलिए उस भूमिका से लिया गया डंप अधूरा निकलेगा। --no-owner पुनर्स्थापित ऑब्जेक्ट का स्वामित्व उसी के पास छोड़ देता है जो पुनर्स्थापन चलाता है, इसलिए इसे स्वामी के रूप में चलाने से स्वामित्व वहीं पहुँचता है जहाँ अनुदान उसकी अपेक्षा करते हैं। नए क्लस्टर पर एक पेच है: pg_dump अनुदान तो साथ ले जाता है, पर जिन भूमिकाओं का नाम वे लेते हैं उन्हें नहीं, इसलिए पुनर्स्थापित करने से पहले snapotter_app बना लें वरना --exit-on-error पहले ही GRANT पर रुक जाएगा। इसके बावजूद SnapOtter अगले बूट पर अनुदान दोबारा लागू कर देता है।

इस डेटाबेस डंप में /data/files या Redis में टिकाऊ BullMQ स्थिति में सहेजे गए लाइब्रेरी ऑब्जेक्ट शामिल नहीं हैं। सुरक्षा और हार्डनिंग में समन्वित प्रक्रिया के साथ उनका बैकअप लें और पुनर्स्थापित करें।

कोल्ड वॉल्यूम स्नैपशॉट

bash
# Stop every service first, then use your storage platform to snapshot the
# PostgreSQL, app-data, and Redis volumes as one crash-consistent set.
docker compose -f docker/docker-compose.yml stop

tar के साथ लाइव PostgreSQL डेटा निर्देशिका की प्रतिलिपि न बनाएं। प्रोजेक्ट के अनुसार उपसर्ग वॉल्यूम नाम लिखें, इसलिए शाब्दिक लेबल SnapOtter-pgdata मानने के बजाय docker inspect या अपने स्टोरेज प्लेटफ़ॉर्म से माउंटेड वॉल्यूम आईडी को हल करें।

1.x (SQLite) से माइग्रेट करना

SnapOtter 1.x से अपग्रेड करने की अपनी अलग गाइड है: Upgrading from 1.x to 2.0 देखें। संक्षेप में, अपने मौजूदा /data वॉल्यूम का पुनः उपयोग करें और 2.0 पहले बूट पर /data/snapotter.db का स्वतः पता लगाकर उसे इम्पोर्ट करता है (या इसे स्पष्ट रूप से इंगित करने के लिए SQLITE_MIGRATE_PATH सेट करें)। पहले पूरे /data वॉल्यूम का बैकअप लें, केवल snapotter.db का नहीं: 1.x SQLite WAL मोड का उपयोग करता है, इसलिए एक रुका हुआ कंटेनर अक्सर अपना अधिकांश डेटा एक लगभग-खाली snapotter.db के बगल में snapotter.db-wal में छोड़ देता है।