rustyroad/writers/migrations/
initial_sql_loader.rs1use crate::{database::Database, writers::write_to_file, Project};
2use bcrypt::{hash as new_hash, DEFAULT_COST};
3use color_eyre::eyre::Result;
4async fn hash_password(password: &str) -> Result<String, bcrypt::BcryptError> {
5 new_hash(password, DEFAULT_COST)
6}
7
8pub async fn load_sql_for_new_project(
9 project: &Project,
10 database: Database,
11) -> Result<Vec<String>, std::io::Error> {
12 let mut statements = Vec::new();
13 let hashed_admin_password = match hash_password("admin").await {
15 Ok(hashed_password) => hashed_password,
16 Err(err) => {
17 eprintln!("Failed to hash admin password: {:?}", err);
18 return Err(std::io::Error::other("Failed to hash admin password"));
19 }
20 };
21
22 match database.database_type {
23 crate::database::DatabaseType::Sqlite => {
24 statements.push(
26 "CREATE TABLE IF NOT EXISTS Users (
27 id INTEGER PRIMARY KEY AUTOINCREMENT,
28 password TEXT NOT NULL,
29 username TEXT NOT NULL UNIQUE,
30 role_id INTEGER,
31 FOREIGN KEY (role_id) REFERENCES Roles(id)
32 );"
33 .to_string(),
34 );
35
36 statements.push(
38 "CREATE TABLE IF NOT EXISTS Roles (
39 id INTEGER PRIMARY KEY AUTOINCREMENT,
40 name TEXT NOT NULL UNIQUE
41 );"
42 .to_string(),
43 );
44
45 statements.push(
47 "CREATE TABLE IF NOT EXISTS Permissions (
48 id INTEGER PRIMARY KEY AUTOINCREMENT,
49 name TEXT NOT NULL UNIQUE,
50 role_id INTEGER NOT NULL,
51 FOREIGN KEY (role_id) REFERENCES Roles(id)
52 );"
53 .to_string(),
54 );
55
56 statements.push(
58 "CREATE TABLE IF NOT EXISTS Sessions (
59 id INTEGER PRIMARY KEY AUTOINCREMENT,
60 user_id INTEGER NOT NULL,
61 session_token TEXT NOT NULL UNIQUE,
62 session_data TEXT,
63 expiration_date DATETIME,
64 FOREIGN KEY (user_id) REFERENCES Users(id)
65 );"
66 .to_string(),
67 );
68
69 statements.push(format!(
71 "INSERT OR IGNORE INTO Roles (name) VALUES ('{}');",
72 "admin"
73 ));
74
75 statements.push(format!(
77 "INSERT OR IGNORE INTO Permissions (name, role_id) VALUES ('{}', 1);",
78 "create_user"
79 ));
80
81 statements.push(format!(
83 "INSERT OR IGNORE INTO Permissions (name, role_id) VALUES ('{}', 1);",
84 "read_user"
85 ));
86
87 statements.push(format!(
89 "INSERT OR IGNORE INTO Users (password, username, role_id) VALUES ('{hashed_admin_password}', 'admin', 1);"
90 ));
91
92 let mut down_statements = Vec::new();
94 down_statements.push(format!("DROP DATABASE {};", project.name));
95
96 for (idx, statement) in down_statements.iter().enumerate() {
98 write_to_file(
99 &(&project.initial_migration_down).to_string(),
100 statement.as_bytes(),
101 )
102 .unwrap_or_else(|why| {
103 panic!(
104 "Failed to write to file {}_{}: {:?}",
105 &project.initial_migration_down,
106 idx,
107 why.kind()
108 )
109 });
110 }
111 }
112 crate::database::DatabaseType::Postgres => {
113 statements.push(
115 "
116CREATE TABLE Roles (
117 id SERIAL PRIMARY KEY,
118 name TEXT NOT NULL UNIQUE
119);"
120 .to_string(),
121 );
122
123 statements.push(
125 "
126CREATE TABLE Users (
127 id SERIAL PRIMARY KEY,
128 password TEXT NOT NULL,
129 username TEXT NOT NULL UNIQUE,
130 role_id INTEGER,
131 FOREIGN KEY (role_id) REFERENCES Roles(id)
132);"
133 .to_string(),
134 );
135
136 statements.push(
138 "
139CREATE TABLE Permissions (
140 id SERIAL PRIMARY KEY,
141 name TEXT NOT NULL UNIQUE,
142 role_id INTEGER NOT NULL,
143 FOREIGN KEY (role_id) REFERENCES Roles(id)
144);"
145 .to_string(),
146 );
147
148 statements.push(
150 "
151CREATE TABLE Sessions (
152 id SERIAL PRIMARY KEY,
153 user_id INTEGER NOT NULL,
154 session_token TEXT NOT NULL UNIQUE,
155 session_data TEXT,
156 expiration_date TIMESTAMP,
157 FOREIGN KEY (user_id) REFERENCES Users(id)
158);"
159 .to_string(),
160 );
161
162 statements.push(format!("INSERT INTO Roles (name) VALUES ('{}');", "admin"));
164
165 statements.push(format!(
167 "INSERT INTO Permissions (name, role_id) VALUES ('{}', 1);",
168 "create_user"
169 ));
170
171 statements.push(format!(
173 "INSERT INTO Permissions (name, role_id) VALUES ('{}', 1);",
174 "read_user"
175 ));
176
177 statements.push(
179 format!(
180 "INSERT INTO Users (password, username, role_id) VALUES ('{hashed_admin_password}', 'admin', 1);"
181 )
182 );
183
184 for (idx, statement) in statements.iter().enumerate() {
186 write_to_file(
187 &(&project.initial_migration_up).to_string(),
188 statement.as_bytes(),
189 )
190 .unwrap_or_else(|why| {
191 panic!(
192 "Failed to write to file {}_{}: {:?}",
193 &project.initial_migration_up,
194 idx,
195 why.kind()
196 )
197 });
198 }
199
200 let mut down_statements = Vec::new();
202 down_statements.push("DROP SCHEMA public CASCADE;".to_string());
203 down_statements.push("CREATE SCHEMA public;".to_string());
204
205 for (idx, statement) in down_statements.iter().enumerate() {
207 write_to_file(
208 &(&project.initial_migration_down).to_string(),
209 statement.as_bytes(),
210 )
211 .unwrap_or_else(|why| {
212 panic!(
213 "Failed to write to file {}_{}: {:?}",
214 &project.initial_migration_down,
215 idx,
216 why.kind()
217 )
218 });
219 }
220 }
221 crate::database::DatabaseType::Mysql => {
222 statements.push(
224 "
225CREATE TABLE Roles (
226 id INT AUTO_INCREMENT PRIMARY KEY,
227 name VARCHAR(255) NOT NULL UNIQUE
228);"
229 .to_string(),
230 );
231
232 statements.push(
234 "
235CREATE TABLE Users (
236 id INT AUTO_INCREMENT PRIMARY KEY,
237 password VARCHAR(255) NOT NULL,
238 username VARCHAR(255) NOT NULL UNIQUE,
239 email VARCHAR(255) UNIQUE,
240 role_id INT,
241 FOREIGN KEY (role_id) REFERENCES Roles(id)
242);"
243 .to_string(),
244 );
245
246 statements.push(
248 "
249CREATE TABLE Permissions (
250 id INT AUTO_INCREMENT PRIMARY KEY,
251 name VARCHAR(255) NOT NULL UNIQUE,
252 role_id INT NOT NULL,
253 FOREIGN KEY (role_id) REFERENCES Roles(id)
254);"
255 .to_string(),
256 );
257
258 statements.push(
260 "
261CREATE TABLE Sessions (
262 id INT AUTO_INCREMENT PRIMARY KEY,
263 user_id INT NOT NULL,
264 session_token VARCHAR(255) NOT NULL UNIQUE,
265 session_data TEXT,
266 expiration_date DATETIME,
267 FOREIGN KEY (user_id) REFERENCES Users(id)
268);"
269 .to_string(),
270 );
271
272 statements.push(format!("INSERT INTO Roles (name) VALUES ('{}');", "admin"));
274
275 statements.push(format!(
277 "INSERT INTO Permissions (name, role_id) VALUES ('{}', 1);",
278 "create_user"
279 ));
280
281 statements.push(format!(
283 "INSERT INTO Permissions (name, role_id) VALUES ('{}', 1);",
284 "read_user"
285 ));
286
287 statements.push(
289 format!(
290 "INSERT INTO Users (password, username, role_id) VALUES ('{hashed_admin_password}', 'admin', 1);"
291 )
292 );
293
294 for (idx, statement) in statements.iter().enumerate() {
296 write_to_file(
297 &(&project.initial_migration_up).to_string(),
298 statement.as_bytes(),
299 )
300 .unwrap_or_else(|why| {
301 panic!(
302 "Failed to write to file {}_{}: {:?}",
303 &project.initial_migration_up,
304 idx,
305 why.kind()
306 )
307 });
308 }
309
310 let mut down_statements = Vec::new();
312 down_statements.push(format!("DROP DATABASE {};", project.name));
313
314 for (idx, statement) in down_statements.iter().enumerate() {
316 write_to_file(
317 &(&project.initial_migration_down).to_string(),
318 statement.as_bytes(),
319 )
320 .unwrap_or_else(|why| {
321 panic!(
322 "Failed to write to file {}_{}: {:?}",
323 &project.initial_migration_down,
324 idx,
325 why.kind()
326 )
327 });
328 }
329 }
330 crate::database::DatabaseType::Mongo => todo!(),
331 }
332
333 Ok(statements)
334}