schema.sql 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. -- PackIt Database Schema
  2. -- Run this once to set up all required tables.
  3. CREATE DATABASE IF NOT EXISTS packit CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
  4. USE packit;
  5. -- Activity groups (categories)
  6. CREATE TABLE IF NOT EXISTS activity_groups (
  7. groupID INT AUTO_INCREMENT PRIMARY KEY,
  8. groupName VARCHAR(100) NOT NULL,
  9. sortOrder INT DEFAULT 0
  10. );
  11. -- Activities
  12. CREATE TABLE IF NOT EXISTS activities (
  13. activityID INT AUTO_INCREMENT PRIMARY KEY,
  14. groupID INT NOT NULL,
  15. activityName VARCHAR(150) NOT NULL,
  16. sortOrder INT DEFAULT 0,
  17. -- Easter eggs: left off the selection page until the visitor finds the
  18. -- trick that reveals them (see index.php). Lists and links work as usual.
  19. hidden TINYINT(1) NOT NULL DEFAULT 0,
  20. FOREIGN KEY (groupID) REFERENCES activity_groups(groupID) ON DELETE CASCADE
  21. );
  22. -- Item groups
  23. CREATE TABLE IF NOT EXISTS item_groups (
  24. groupID INT AUTO_INCREMENT PRIMARY KEY,
  25. groupName VARCHAR(100) NOT NULL,
  26. sortOrder INT DEFAULT 0
  27. );
  28. -- Items
  29. CREATE TABLE IF NOT EXISTS items (
  30. itemID INT AUTO_INCREMENT PRIMARY KEY,
  31. groupID INT NOT NULL,
  32. itemName VARCHAR(150) NOT NULL,
  33. sortOrder INT DEFAULT 0,
  34. FOREIGN KEY (groupID) REFERENCES item_groups(groupID) ON DELETE CASCADE
  35. );
  36. -- Relation between activities and items
  37. CREATE TABLE IF NOT EXISTS activity_item_map (
  38. activityID INT NOT NULL,
  39. itemID INT NOT NULL,
  40. quantity INT NOT NULL DEFAULT 1,
  41. PRIMARY KEY (activityID, itemID),
  42. FOREIGN KEY (activityID) REFERENCES activities(activityID) ON DELETE CASCADE,
  43. FOREIGN KEY (itemID) REFERENCES items(itemID) ON DELETE CASCADE
  44. );
  45. -- Stored selection hashes (for shareable URLs)
  46. -- hash is a 5-character SHA-256 prefix, grown one character at a time by
  47. -- list.php when a shorter prefix is already taken by a different selection.
  48. CREATE TABLE IF NOT EXISTS selection_hashes (
  49. hashID INT AUTO_INCREMENT PRIMARY KEY,
  50. hash VARCHAR(16) NOT NULL UNIQUE,
  51. activity_ids VARCHAR(500) NOT NULL,
  52. created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  53. INDEX idx_activity_ids (activity_ids)
  54. );
  55. -- Admin users
  56. CREATE TABLE IF NOT EXISTS admin_users (
  57. adminID INT AUTO_INCREMENT PRIMARY KEY,
  58. username VARCHAR(50) NOT NULL UNIQUE,
  59. password_hash VARCHAR(255) NOT NULL
  60. );
  61. -- Failed admin logins, used by admin/login.php to throttle brute-force
  62. -- attempts. Rows older than a day are pruned opportunistically on each write.
  63. CREATE TABLE IF NOT EXISTS login_attempts (
  64. attemptID INT AUTO_INCREMENT PRIMARY KEY,
  65. ip VARCHAR(45) NOT NULL,
  66. username VARCHAR(50) NOT NULL,
  67. attempted_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  68. INDEX idx_ip_time (ip, attempted_at),
  69. INDEX idx_user_time (username, attempted_at)
  70. );
  71. -- -----------------------------------------------
  72. -- Sample data
  73. -- -----------------------------------------------
  74. INSERT INTO activity_groups (groupName, sortOrder) VALUES
  75. ('Overnight', 1), -- groupID 1
  76. ('Rescue & Safety', 2), -- groupID 2
  77. ('Water Activities', 3), -- groupID 3
  78. ('Winter Sports', 4), -- groupID 4
  79. ('Überleben', 5); -- groupID 5 (only hidden activities)
  80. -- activityID 1=Camping 2=Hiking 3=Bivouac 4=First Aid 5=Mountain Rescue
  81. -- activityID 6=Kayaking 7=Swimming 8=Skiing 9=Snowshoeing
  82. -- activityID 10=Zombie-Apokalypse (hidden easter egg)
  83. INSERT INTO activities (groupID, activityName, sortOrder, hidden) VALUES
  84. (1, 'Camping', 1, 0),
  85. (1, 'Hiking', 2, 0),
  86. (1, 'Bivouac', 3, 0),
  87. (2, 'First Aid', 1, 0),
  88. (2, 'Mountain Rescue', 2, 0),
  89. (3, 'Kayaking', 1, 0),
  90. (3, 'Swimming', 2, 0),
  91. (4, 'Skiing', 1, 0),
  92. (4, 'Snowshoeing', 2, 0),
  93. (5, 'Zombie-Apokalypse', 1, 1);
  94. INSERT INTO item_groups (groupName, sortOrder) VALUES
  95. ('Clothing', 1), -- groupID 1
  96. ('Shelter', 2), -- groupID 2
  97. ('Navigation', 3), -- groupID 3
  98. ('Medical', 4), -- groupID 4
  99. ('Water & Food', 5), -- groupID 5
  100. ('Safety Gear', 6), -- groupID 6
  101. ('Zombie-Abwehr', 7); -- groupID 7 (Zombie-Apokalypse only)
  102. -- Items with their resulting auto_increment IDs:
  103. -- 1 Base layer top (Clothing)
  104. -- 2 Base layer bottom (Clothing)
  105. -- 3 Fleece jacket (Clothing)
  106. -- 4 Rain jacket (Clothing)
  107. -- 5 Gloves (Clothing)
  108. -- 6 Warm hat (Clothing)
  109. -- 7 Hiking boots (Clothing)
  110. -- 8 Wetsuit (Clothing)
  111. -- 9 Ski jacket (Clothing)
  112. -- 10 Ski pants (Clothing)
  113. -- 11 Ski boots (Clothing)
  114. -- 12 Tent (Shelter)
  115. -- 13 Sleeping bag (Shelter)
  116. -- 14 Sleeping mat (Shelter)
  117. -- 15 Bivouac bag (Shelter)
  118. -- 16 Topographic map (Navigation)
  119. -- 17 Compass (Navigation)
  120. -- 18 GPS device (Navigation)
  121. -- 19 First aid kit (Medical)
  122. -- 20 Emergency blanket (Medical)
  123. -- 21 Tourniquet (Medical)
  124. -- 22 SAM splint (Medical)
  125. -- 23 Water bottle (Water & Food)
  126. -- 24 Water filter (Water & Food)
  127. -- 25 Trail snacks (Water & Food)
  128. -- 26 Stove & fuel (Water & Food)
  129. -- 27 Cooking pot (Water & Food)
  130. -- 28 Headlamp + batteries (Safety Gear)
  131. -- 29 Whistle (Safety Gear)
  132. -- 30 Rope (30m) (Safety Gear)
  133. -- 31 Avalanche probe (Safety Gear)
  134. -- 32 Avalanche transceiver (Safety Gear)
  135. -- 33 Shovel (Safety Gear)
  136. -- 34 Life jacket (Safety Gear)
  137. -- 35 Paddle float (Safety Gear)
  138. -- 36-53 Zombie-Abwehr items, used only by the hidden Zombie-Apokalypse
  139. INSERT INTO items (groupID, itemName, sortOrder) VALUES
  140. (1, 'Base layer top', 1),
  141. (1, 'Base layer bottom', 2),
  142. (1, 'Fleece jacket', 3),
  143. (1, 'Rain jacket', 4),
  144. (1, 'Gloves', 5),
  145. (1, 'Warm hat', 6),
  146. (1, 'Hiking boots', 7),
  147. (1, 'Wetsuit', 8),
  148. (1, 'Ski jacket', 9),
  149. (1, 'Ski pants', 10),
  150. (1, 'Ski boots', 11),
  151. (2, 'Tent', 1),
  152. (2, 'Sleeping bag', 2),
  153. (2, 'Sleeping mat', 3),
  154. (2, 'Bivouac bag', 4),
  155. (3, 'Topographic map', 1),
  156. (3, 'Compass', 2),
  157. (3, 'GPS device', 3),
  158. (4, 'First aid kit', 1),
  159. (4, 'Emergency blanket', 2),
  160. (4, 'Tourniquet', 3),
  161. (4, 'SAM splint', 4),
  162. (5, 'Water bottle', 1),
  163. (5, 'Water filter', 2),
  164. (5, 'Trail snacks', 3),
  165. (5, 'Stove & fuel', 4),
  166. (5, 'Cooking pot', 5),
  167. (6, 'Headlamp + spare batteries', 1),
  168. (6, 'Whistle', 2),
  169. (6, 'Rope (30m)', 3),
  170. (6, 'Avalanche probe', 4),
  171. (6, 'Avalanche transceiver', 5),
  172. (6, 'Shovel', 6),
  173. (6, 'Life jacket', 7),
  174. (6, 'Paddle float', 8),
  175. (7, 'Laufschuhe (Regel Nr. 1: Cardio)', 1), -- 36
  176. (7, 'Bissfeste Armschützer (Paar)', 2), -- 37
  177. (7, 'Dosenbohnen', 3), -- 38
  178. (7, 'Twinkies (letzte Packung der Welt)', 4), -- 39
  179. (7, 'Dosenöffner (manuell)', 5), -- 40
  180. (7, 'Feuerzeug', 6), -- 41
  181. (7, 'Funkgerät', 7), -- 42
  182. (7, 'Kurbelradio', 8), -- 43
  183. (7, 'Multitool', 9), -- 44
  184. (7, 'Karte mit sicheren Zonen', 10), -- 45
  185. (7, 'Zombie-Wörterbuch (Grrr = Hallo)', 11), -- 46
  186. (7, 'Glaubwürdige Ausrede für die Bisswunde', 12), -- 47
  187. (7, 'Cricketschläger', 13), -- 48
  188. (7, 'Brecheisen', 14), -- 49
  189. (7, 'Panzertape', 15), -- 50
  190. (7, 'Bolzenschneider', 16), -- 51
  191. (7, 'Knoblauch (hilft nicht, beruhigt aber)', 17), -- 52
  192. (7, 'Gartenzwerg zur Ablenkung', 18); -- 53
  193. -- -----------------------------------------------
  194. -- Activity -> Item mappings (all IDs verified)
  195. -- -----------------------------------------------
  196. -- activityID 1: Camping
  197. INSERT INTO activity_item_map (activityID, itemID) VALUES
  198. (1, 1),(1, 2),(1, 3),(1, 4),(1, 7),
  199. (1,12),(1,13),(1,14),
  200. (1,16),(1,17),
  201. (1,19),(1,20),
  202. (1,23),(1,24),(1,25),(1,26),(1,27),
  203. (1,28),(1,29);
  204. -- activityID 2: Hiking
  205. INSERT INTO activity_item_map (activityID, itemID) VALUES
  206. (2, 1),(2, 2),(2, 3),(2, 4),(2, 7),
  207. (2,16),(2,17),(2,18),
  208. (2,19),(2,20),
  209. (2,23),(2,24),(2,25),
  210. (2,28),(2,29);
  211. -- activityID 3: Bivouac
  212. INSERT INTO activity_item_map (activityID, itemID) VALUES
  213. (3, 1),(3, 2),(3, 3),(3, 4),(3, 5),(3, 6),(3, 7),
  214. (3,14),(3,15),
  215. (3,16),(3,17),
  216. (3,19),(3,20),
  217. (3,23),(3,25),
  218. (3,28),(3,29);
  219. -- activityID 4: First Aid
  220. INSERT INTO activity_item_map (activityID, itemID) VALUES
  221. (4,19),(4,20),(4,21),(4,22),
  222. (4,28),(4,29);
  223. -- activityID 5: Mountain Rescue
  224. INSERT INTO activity_item_map (activityID, itemID) VALUES
  225. (5, 1),(5, 3),(5, 4),(5, 5),(5, 6),
  226. (5,16),(5,17),(5,18),
  227. (5,19),(5,20),(5,21),(5,22),
  228. (5,28),(5,29),(5,30);
  229. -- activityID 6: Kayaking
  230. INSERT INTO activity_item_map (activityID, itemID) VALUES
  231. (6, 1),(6, 4),(6, 8),
  232. (6,19),(6,20),
  233. (6,23),(6,24),
  234. (6,28),(6,29),
  235. (6,34),(6,35);
  236. -- activityID 7: Swimming
  237. INSERT INTO activity_item_map (activityID, itemID) VALUES
  238. (7, 8),
  239. (7,19),(7,20),
  240. (7,34);
  241. -- activityID 8: Skiing
  242. INSERT INTO activity_item_map (activityID, itemID) VALUES
  243. (8, 1),(8, 2),(8, 5),(8, 6),(8, 9),(8,10),(8,11),
  244. (8,16),(8,17),(8,18),
  245. (8,19),(8,20),
  246. (8,23),(8,25),
  247. (8,28),(8,29),
  248. (8,31),(8,32),(8,33);
  249. -- activityID 9: Snowshoeing
  250. INSERT INTO activity_item_map (activityID, itemID) VALUES
  251. (9, 1),(9, 2),(9, 3),(9, 4),(9, 5),(9, 6),(9, 7),
  252. (9,16),(9,17),
  253. (9,19),(9,20),
  254. (9,23),(9,24),(9,25),
  255. (9,28),(9,29),
  256. (9,31);
  257. -- activityID 10: Zombie-Apokalypse (hidden)
  258. INSERT INTO activity_item_map (activityID, itemID, quantity) VALUES
  259. (10,13, 1),(10,19, 1),(10,23, 2),(10,28, 1),
  260. (10,36, 1),(10,37, 1),(10,38,12),(10,39, 1),(10,40, 1),(10,41, 1),
  261. (10,42, 2),(10,43, 1),(10,44, 1),(10,45, 1),(10,46, 1),(10,47, 1),
  262. (10,48, 1),(10,49, 1),(10,50, 1),(10,51, 1),(10,52, 1),(10,53, 1);
  263. -- -----------------------------------------------
  264. -- Default admin user
  265. -- Username: admin Password: packit-admin
  266. -- Change the password after first login!
  267. -- -----------------------------------------------
  268. INSERT INTO admin_users (username, password_hash) VALUES
  269. ('admin', '$2y$12$eTfGi/R5IxHH5NQxrVe9.OijNfyFfpvT/X1i9aYgqZPRF6.Gu72hC');