[{"data":1,"prerenderedAt":711},["ShallowReactive",2],{"navigation_docs":3,"-developer-database":218,"-developer-database-surround":706},[4,20,45,75,95,105,140,168,198],{"title":5,"path":6,"stem":7,"children":8,"page":19},"Introduction","\u002Fintroduction","1.introduction",[9,14],{"title":10,"path":11,"stem":12,"icon":13},"What is GO?","\u002Fintroduction\u002Fwhat-is-go","1.introduction\u002F1.what-is-go","i-lucide-info",{"title":15,"path":16,"stem":17,"icon":18},"Features","\u002Fintroduction\u002Ffeatures","1.introduction\u002F2.features","i-lucide-sparkles",false,{"title":21,"path":22,"stem":23,"children":24,"page":19},"Getting Started","\u002Fgetting-started","2.getting-started",[25,30,35,40],{"title":26,"path":27,"stem":28,"icon":29},"Installation","\u002Fgetting-started\u002Finstallation","2.getting-started\u002F1.installation","i-lucide-download",{"title":31,"path":32,"stem":33,"icon":34},"Configuration","\u002Fgetting-started\u002Fconfiguration","2.getting-started\u002F2.configuration","i-lucide-settings",{"title":36,"path":37,"stem":38,"icon":39},"First Setup","\u002Fgetting-started\u002Ffirst-setup","2.getting-started\u002F3.first-setup","i-lucide-rocket",{"title":41,"path":42,"stem":43,"icon":44},"Updating","\u002Fgetting-started\u002Fupdating","2.getting-started\u002F4.updating","i-lucide-refresh-cw",{"title":46,"icon":19,"path":47,"stem":48,"children":49,"page":19},"User Guide","\u002Fuser-guide","3.user-guide",[50,55,60,65,70],{"title":51,"path":52,"stem":53,"icon":54},"Creating a Short Link","\u002Fuser-guide\u002Fcreating-a-short-link","3.user-guide\u002F1.creating-a-short-link","i-lucide-link",{"title":56,"path":57,"stem":58,"icon":59},"Expiration","\u002Fuser-guide\u002Fexpiration","3.user-guide\u002F2.expiration","i-lucide-clock",{"title":61,"path":62,"stem":63,"icon":64},"Renewing Links","\u002Fuser-guide\u002Frenewing-links","3.user-guide\u002F3.renewing-links","i-lucide-repeat",{"title":66,"path":67,"stem":68,"icon":69},"Dashboard","\u002Fuser-guide\u002Fdashboard","3.user-guide\u002F4.dashboard","i-lucide-layout-dashboard",{"title":71,"path":72,"stem":73,"icon":74},"Analytics","\u002Fuser-guide\u002Fanalytics","3.user-guide\u002F5.analytics","i-lucide-bar-chart-3",{"title":76,"path":77,"stem":78,"children":79,"page":19},"Authentication","\u002Fauthentication","4.authentication",[80,85,90],{"title":81,"path":82,"stem":83,"icon":84},"Email Login","\u002Fauthentication\u002Femail-login","4.authentication\u002F1.email-login","i-lucide-mail",{"title":86,"path":87,"stem":88,"icon":89},"Google Login","\u002Fauthentication\u002Fgoogle-login","4.authentication\u002F2.google-login","i-lucide-globe",{"title":91,"path":92,"stem":93,"icon":94},"Firebase Setup","\u002Fauthentication\u002Ffirebase-setup","4.authentication\u002F3.firebase-setup","i-simple-icons-firebase",{"title":96,"path":97,"stem":98,"children":99,"page":19},"Captcha","\u002Fcaptcha","5.captcha",[100],{"title":101,"path":102,"stem":103,"icon":104},"Cloudflare Turnstile","\u002Fcaptcha\u002Fcloudflare-turnstile","5.captcha\u002F1.cloudflare-turnstile","i-simple-icons-cloudflare",{"title":106,"path":107,"stem":108,"children":109,"page":19},"Administration","\u002Fadministration","6.administration",[110,115,120,125,130,135],{"title":111,"path":112,"stem":113,"icon":114},"Admin Panel","\u002Fadministration\u002Fadmin-panel","6.administration\u002F1.admin-panel","i-lucide-shield",{"title":116,"path":117,"stem":118,"icon":119},"Users","\u002Fadministration\u002Fusers","6.administration\u002F2.users","i-lucide-users",{"title":121,"path":122,"stem":123,"icon":124},"Links","\u002Fadministration\u002Flinks","6.administration\u002F3.links","i-lucide-link-2",{"title":126,"path":127,"stem":128,"icon":129},"Reports","\u002Fadministration\u002Freports","6.administration\u002F4.reports","i-lucide-flag",{"title":131,"path":132,"stem":133,"icon":134},"Bans & Timeouts","\u002Fadministration\u002Fbans-and-timeouts","6.administration\u002F5.bans-and-timeouts","i-lucide-user-x",{"title":136,"path":137,"stem":138,"icon":139},"Audit Logs","\u002Fadministration\u002Faudit-logs","6.administration\u002F6.audit-logs","i-lucide-scroll",{"title":141,"path":142,"stem":143,"children":144,"page":19},"Deployment","\u002Fdeployment","7.deployment",[145,150,155,160,165],{"title":146,"path":147,"stem":148,"icon":149},"VPS Installation","\u002Fdeployment\u002Fvps-installation","7.deployment\u002F1.vps-installation","i-lucide-server",{"title":151,"path":152,"stem":153,"icon":154},"Environment Variables","\u002Fdeployment\u002Fenvironment-variables","7.deployment\u002F2.environment-variables","i-lucide-sliders",{"title":156,"path":157,"stem":158,"icon":159},"Cloudflare Tunnel","\u002Fdeployment\u002Fcloudflare-tunnel","7.deployment\u002F3.cloudflare-tunnel","i-lucide-network",{"title":161,"path":162,"stem":163,"icon":164},"Systemd","\u002Fdeployment\u002Fsystemd","7.deployment\u002F4.systemd","i-lucide-cpu",{"title":41,"path":166,"stem":167,"icon":44},"\u002Fdeployment\u002Fupdating","7.deployment\u002F5.updating",{"title":169,"path":170,"stem":171,"children":172,"page":19},"Developer","\u002Fdeveloper","8.developer",[173,178,183,188,193],{"title":174,"path":175,"stem":176,"icon":177},"Architecture","\u002Fdeveloper\u002Farchitecture","8.developer\u002F1.architecture","i-lucide-box",{"title":179,"path":180,"stem":181,"icon":182},"API Architecture","\u002Fdeveloper\u002Fapi","8.developer\u002F2.api","i-lucide-code-xml",{"title":184,"path":185,"stem":186,"icon":187},"Database","\u002Fdeveloper\u002Fdatabase","8.developer\u002F3.database","i-lucide-database",{"title":189,"path":190,"stem":191,"icon":192},"Development","\u002Fdeveloper\u002Fdevelopment","8.developer\u002F4.development","i-lucide-terminal",{"title":194,"path":195,"stem":196,"icon":197},"Building Releases","\u002Fdeveloper\u002Fbuilding-releases","8.developer\u002F5.building-releases","i-lucide-package-check",{"title":199,"path":200,"stem":201,"children":202,"page":19},"Reference","\u002Freference","9.reference",[203,208,213],{"title":204,"path":205,"stem":206,"icon":207},"Configuration Reference","\u002Freference\u002Fconfiguration","9.reference\u002F1.configuration","i-lucide-file-cog",{"title":209,"path":210,"stem":211,"icon":212},"API Reference","\u002Freference\u002Fapi-reference","9.reference\u002F2.api-reference","i-lucide-webhook",{"title":214,"path":215,"stem":216,"icon":217},"Changelog","\u002Freference\u002Fchangelog","9.reference\u002F3.changelog","i-lucide-history",{"id":219,"title":184,"body":220,"description":697,"extension":698,"links":699,"meta":700,"navigation":701,"path":185,"seo":702,"stem":186,"__hash__":705},"docs\u002F8.developer\u002F3.database.md",{"type":221,"value":222,"toc":680},"minimark",[223,227,241,244,249,260,301,335,337,341,349,352,424,431,434,532,539,542,596,603,606,649,660,663,673,676],[224,225,184],"h1",{"id":226},"database",[228,229,230,231,235,236,240],"p",{},"GO Shortener uses ",[232,233,234],"strong",{},"SQLite"," in Write-Ahead Logging (WAL) mode via the CGO-free ",[237,238,239],"code",{},"modernc.org\u002Fsqlite"," driver. This provides the performance of an embedded database with zero external database server administration.",[242,243],"hr",{},[245,246,248],"h2",{"id":247},"performance-pragmas-concurrency","Performance Pragmas & Concurrency",[228,250,251,252,255,256,259],{},"At startup, the database connection (",[237,253,254],{},"InitDB"," in ",[237,257,258],{},"internal\u002Fdatabase\u002Fdb.go",") executes essential pragmas:",[261,262,267],"pre",{"className":263,"code":264,"language":265,"meta":266,"style":266},"language-sql shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","PRAGMA journal_mode=WAL;\nPRAGMA busy_timeout=5000;\nPRAGMA foreign_keys=ON;\nPRAGMA synchronous=NORMAL;\nPRAGMA cache_size=-20000; -- 20MB in-memory page cache\n","sql","",[237,268,269,277,283,289,295],{"__ignoreMap":266},[270,271,274],"span",{"class":272,"line":273},"line",1,[270,275,276],{},"PRAGMA journal_mode=WAL;\n",[270,278,280],{"class":272,"line":279},2,[270,281,282],{},"PRAGMA busy_timeout=5000;\n",[270,284,286],{"class":272,"line":285},3,[270,287,288],{},"PRAGMA foreign_keys=ON;\n",[270,290,292],{"class":272,"line":291},4,[270,293,294],{},"PRAGMA synchronous=NORMAL;\n",[270,296,298],{"class":272,"line":297},5,[270,299,300],{},"PRAGMA cache_size=-20000; -- 20MB in-memory page cache\n",[302,303,304,315,329],"ul",{},[305,306,307,310,311,314],"li",{},[232,308,309],{},"Write-Ahead Logging (WAL)",": Writes are appended to a ",[237,312,313],{},"-wal"," file while reads continue concurrently against the main database file without blocking.",[305,316,317,320,321,324,325,328],{},[232,318,319],{},"Single-Writer Pool",": ",[237,322,323],{},"db.SetMaxOpenConns(1)"," restricts write operations to a single worker, completely avoiding ",[237,326,327],{},"SQLITE_BUSY"," lock contention.",[305,330,331,334],{},[232,332,333],{},"Synchronous Normal",": Balances durability and high IOPS without risking database corruption.",[242,336],{},[245,338,340],{"id":339},"core-schema-definitions","Core Schema Definitions",[342,343,345,346],"h3",{"id":344},"_1-links","1. ",[237,347,348],{},"links",[228,350,351],{},"Stores all generated short links:",[261,353,355],{"className":263,"code":354,"language":265,"meta":266,"style":266},"CREATE TABLE IF NOT EXISTS links (\n    id TEXT PRIMARY KEY,\n    short_code TEXT UNIQUE NOT NULL,\n    destination_url TEXT NOT NULL,\n    owner_id TEXT,\n    created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    expires_at DATETIME NOT NULL,\n    deleted_at DATETIME,\n    status TEXT NOT NULL DEFAULT 'ACTIVE',\n    auto_renew INTEGER NOT NULL DEFAULT 0,\n    click_count INTEGER NOT NULL DEFAULT 0\n);\n",[237,356,357,362,367,372,377,382,388,394,400,406,412,418],{"__ignoreMap":266},[270,358,359],{"class":272,"line":273},[270,360,361],{},"CREATE TABLE IF NOT EXISTS links (\n",[270,363,364],{"class":272,"line":279},[270,365,366],{},"    id TEXT PRIMARY KEY,\n",[270,368,369],{"class":272,"line":285},[270,370,371],{},"    short_code TEXT UNIQUE NOT NULL,\n",[270,373,374],{"class":272,"line":291},[270,375,376],{},"    destination_url TEXT NOT NULL,\n",[270,378,379],{"class":272,"line":297},[270,380,381],{},"    owner_id TEXT,\n",[270,383,385],{"class":272,"line":384},6,[270,386,387],{},"    created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n",[270,389,391],{"class":272,"line":390},7,[270,392,393],{},"    expires_at DATETIME NOT NULL,\n",[270,395,397],{"class":272,"line":396},8,[270,398,399],{},"    deleted_at DATETIME,\n",[270,401,403],{"class":272,"line":402},9,[270,404,405],{},"    status TEXT NOT NULL DEFAULT 'ACTIVE',\n",[270,407,409],{"class":272,"line":408},10,[270,410,411],{},"    auto_renew INTEGER NOT NULL DEFAULT 0,\n",[270,413,415],{"class":272,"line":414},11,[270,416,417],{},"    click_count INTEGER NOT NULL DEFAULT 0\n",[270,419,421],{"class":272,"line":420},12,[270,422,423],{},");\n",[342,425,427,428],{"id":426},"_2-users","2. ",[237,429,430],{},"users",[228,432,433],{},"Stores registered user accounts:",[261,435,437],{"className":263,"code":436,"language":265,"meta":266,"style":266},"CREATE TABLE IF NOT EXISTS users (\n    id TEXT PRIMARY KEY,\n    first_name TEXT NOT NULL,\n    last_name TEXT NOT NULL,\n    email TEXT UNIQUE NOT NULL,\n    password_hash TEXT,\n    auth_provider TEXT NOT NULL DEFAULT 'email',\n    firebase_uid TEXT,\n    role TEXT NOT NULL DEFAULT 'user',\n    status TEXT NOT NULL DEFAULT 'active',\n    timeout_until DATETIME,\n    timeout_reason TEXT,\n    ban_reason TEXT,\n    quota_limit INTEGER NOT NULL DEFAULT 100,\n    created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    last_login_at DATETIME\n);\n",[237,438,439,444,448,453,458,463,468,473,478,483,488,493,498,504,510,515,521,527],{"__ignoreMap":266},[270,440,441],{"class":272,"line":273},[270,442,443],{},"CREATE TABLE IF NOT EXISTS users (\n",[270,445,446],{"class":272,"line":279},[270,447,366],{},[270,449,450],{"class":272,"line":285},[270,451,452],{},"    first_name TEXT NOT NULL,\n",[270,454,455],{"class":272,"line":291},[270,456,457],{},"    last_name TEXT NOT NULL,\n",[270,459,460],{"class":272,"line":297},[270,461,462],{},"    email TEXT UNIQUE NOT NULL,\n",[270,464,465],{"class":272,"line":384},[270,466,467],{},"    password_hash TEXT,\n",[270,469,470],{"class":272,"line":390},[270,471,472],{},"    auth_provider TEXT NOT NULL DEFAULT 'email',\n",[270,474,475],{"class":272,"line":396},[270,476,477],{},"    firebase_uid TEXT,\n",[270,479,480],{"class":272,"line":402},[270,481,482],{},"    role TEXT NOT NULL DEFAULT 'user',\n",[270,484,485],{"class":272,"line":408},[270,486,487],{},"    status TEXT NOT NULL DEFAULT 'active',\n",[270,489,490],{"class":272,"line":414},[270,491,492],{},"    timeout_until DATETIME,\n",[270,494,495],{"class":272,"line":420},[270,496,497],{},"    timeout_reason TEXT,\n",[270,499,501],{"class":272,"line":500},13,[270,502,503],{},"    ban_reason TEXT,\n",[270,505,507],{"class":272,"line":506},14,[270,508,509],{},"    quota_limit INTEGER NOT NULL DEFAULT 100,\n",[270,511,513],{"class":272,"line":512},15,[270,514,387],{},[270,516,518],{"class":272,"line":517},16,[270,519,520],{},"    updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n",[270,522,524],{"class":272,"line":523},17,[270,525,526],{},"    last_login_at DATETIME\n",[270,528,530],{"class":272,"line":529},18,[270,531,423],{},[342,533,535,536],{"id":534},"_3-link_clicks","3. ",[237,537,538],{},"link_clicks",[228,540,541],{},"Stores privacy-preserving redirect events:",[261,543,545],{"className":263,"code":544,"language":265,"meta":266,"style":266},"CREATE TABLE IF NOT EXISTS link_clicks (\n    id INTEGER PRIMARY KEY AUTOINCREMENT,\n    link_id TEXT NOT NULL REFERENCES links(id) ON DELETE CASCADE,\n    clicked_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    country TEXT DEFAULT 'Unknown',\n    referrer TEXT,\n    device_type TEXT,\n    browser TEXT,\n    os TEXT\n);\n",[237,546,547,552,557,562,567,572,577,582,587,592],{"__ignoreMap":266},[270,548,549],{"class":272,"line":273},[270,550,551],{},"CREATE TABLE IF NOT EXISTS link_clicks (\n",[270,553,554],{"class":272,"line":279},[270,555,556],{},"    id INTEGER PRIMARY KEY AUTOINCREMENT,\n",[270,558,559],{"class":272,"line":285},[270,560,561],{},"    link_id TEXT NOT NULL REFERENCES links(id) ON DELETE CASCADE,\n",[270,563,564],{"class":272,"line":291},[270,565,566],{},"    clicked_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n",[270,568,569],{"class":272,"line":297},[270,570,571],{},"    country TEXT DEFAULT 'Unknown',\n",[270,573,574],{"class":272,"line":384},[270,575,576],{},"    referrer TEXT,\n",[270,578,579],{"class":272,"line":390},[270,580,581],{},"    device_type TEXT,\n",[270,583,584],{"class":272,"line":396},[270,585,586],{},"    browser TEXT,\n",[270,588,589],{"class":272,"line":402},[270,590,591],{},"    os TEXT\n",[270,593,594],{"class":272,"line":408},[270,595,423],{},[342,597,599,600],{"id":598},"_4-quota_usage","4. ",[237,601,602],{},"quota_usage",[228,604,605],{},"Tracks sliding quota windows without storing raw IP addresses:",[261,607,609],{"className":263,"code":608,"language":265,"meta":266,"style":266},"CREATE TABLE IF NOT EXISTS quota_usage (\n    id INTEGER PRIMARY KEY AUTOINCREMENT,\n    identity_key TEXT NOT NULL,\n    is_anonymous INTEGER NOT NULL,\n    window_start TEXT NOT NULL,\n    count INTEGER NOT NULL DEFAULT 1,\n    UNIQUE(identity_key, window_start)\n);\n",[237,610,611,616,620,625,630,635,640,645],{"__ignoreMap":266},[270,612,613],{"class":272,"line":273},[270,614,615],{},"CREATE TABLE IF NOT EXISTS quota_usage (\n",[270,617,618],{"class":272,"line":279},[270,619,556],{},[270,621,622],{"class":272,"line":285},[270,623,624],{},"    identity_key TEXT NOT NULL,\n",[270,626,627],{"class":272,"line":291},[270,628,629],{},"    is_anonymous INTEGER NOT NULL,\n",[270,631,632],{"class":272,"line":297},[270,633,634],{},"    window_start TEXT NOT NULL,\n",[270,636,637],{"class":272,"line":384},[270,638,639],{},"    count INTEGER NOT NULL DEFAULT 1,\n",[270,641,642],{"class":272,"line":390},[270,643,644],{},"    UNIQUE(identity_key, window_start)\n",[270,646,647],{"class":272,"line":396},[270,648,423],{},[342,650,652,653,656,657],{"id":651},"_5-reports-permanent_link_requests","5. ",[237,654,655],{},"reports"," & ",[237,658,659],{},"permanent_link_requests",[228,661,662],{},"Queue tables for moderation and administrator approval workflows.",[342,664,666,667,656,670],{"id":665},"_6-login_records-admin_audit_logs","6. ",[237,668,669],{},"login_records",[237,671,672],{},"admin_audit_logs",[228,674,675],{},"Tamper-resistant audit trails capturing timestamps, account identifiers, SHA-256 IP hashes, User-Agents, and action resolutions.",[677,678,679],"style",{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":266,"searchDepth":279,"depth":279,"links":681},[682,683],{"id":247,"depth":279,"text":248},{"id":339,"depth":279,"text":340,"children":684},[685,687,689,691,693,695],{"id":344,"depth":285,"text":686},"1. links",{"id":426,"depth":285,"text":688},"2. users",{"id":534,"depth":285,"text":690},"3. link_clicks",{"id":598,"depth":285,"text":692},"4. quota_usage",{"id":651,"depth":285,"text":694},"5. reports & permanent_link_requests",{"id":665,"depth":285,"text":696},"6. login_records & admin_audit_logs","SQLite engine configuration, WAL mode, schema design, and table definitions.","md",null,{},{"icon":187},{"title":703,"description":704},"Database Schema & SQLite - GO Shortener Developer Guide","Technical documentation for GO Shortener's pure Go SQLite storage engine, pragmas, schema tables, and indexing strategy.","PbzNPvrUCyUxfczcaLWFsM9iH7t3FMyqhmBuAZBVZSU",[707,709],{"title":179,"path":180,"stem":181,"description":708,"icon":182,"children":-1},"REST conventions, standard response formats, and error handling policies.",{"title":189,"path":190,"stem":191,"description":710,"icon":192,"children":-1},"Setting up a local development environment, running tests, and compiling the project.",1788462428695]