11use actix_cors:: Cors ;
22use actix_web:: { web, App , HttpResponse , HttpServer } ;
33use anyhow:: Result ;
4+ use clap:: Parser ;
45use rust_embed:: RustEmbed ;
56use simplelink:: check_and_generate_admin_token;
7+ use simplelink:: models:: DatabasePool ;
68use simplelink:: { create_db_pool, run_migrations} ;
79use simplelink:: { handlers, AppState } ;
810use sqlx:: { Postgres , Sqlite } ;
@@ -26,6 +28,106 @@ async fn serve_static_file(path: &str) -> HttpResponse {
2628 }
2729}
2830
31+ async fn create_initial_links ( pool : & DatabasePool ) -> Result < ( ) > {
32+ if let Ok ( links) = std:: env:: var ( "INITIAL_LINKS" ) {
33+ for link_entry in links. split ( ';' ) {
34+ let parts: Vec < & str > = link_entry. split ( ',' ) . collect ( ) ;
35+ if parts. len ( ) >= 2 {
36+ let url = parts[ 0 ] ;
37+ let code = parts[ 1 ] ;
38+
39+ match pool {
40+ DatabasePool :: Postgres ( pool) => {
41+ sqlx:: query (
42+ "INSERT INTO links (original_url, short_code, user_id)
43+ VALUES ($1, $2, $3)
44+ ON CONFLICT (short_code)
45+ DO UPDATE SET short_code = EXCLUDED.short_code
46+ WHERE links.original_url = EXCLUDED.original_url" ,
47+ )
48+ . bind ( url)
49+ . bind ( code)
50+ . bind ( 1 )
51+ . execute ( pool)
52+ . await ?;
53+ }
54+ DatabasePool :: Sqlite ( pool) => {
55+ // First check if the exact combination exists
56+ let exists = sqlx:: query_scalar :: < _ , bool > (
57+ "SELECT EXISTS(
58+ SELECT 1 FROM links
59+ WHERE original_url = ?1
60+ AND short_code = ?2
61+ )" ,
62+ )
63+ . bind ( url)
64+ . bind ( code)
65+ . fetch_one ( pool)
66+ . await ?;
67+
68+ // Only insert if the exact combination doesn't exist
69+ if !exists {
70+ sqlx:: query (
71+ "INSERT INTO links (original_url, short_code, user_id)
72+ VALUES (?1, ?2, ?3)" ,
73+ )
74+ . bind ( url)
75+ . bind ( code)
76+ . bind ( 1 )
77+ . execute ( pool)
78+ . await ?;
79+ info ! ( "Created initial link: {} -> {} for user_id: 1" , code, url) ;
80+ } else {
81+ info ! ( "Skipped existing link: {} -> {} for user_id: 1" , code, url) ;
82+ }
83+ }
84+ }
85+ }
86+ }
87+ }
88+ Ok ( ( ) )
89+ }
90+
91+ async fn create_admin_user ( pool : & DatabasePool , email : & str , password : & str ) -> Result < ( ) > {
92+ use argon2:: {
93+ password_hash:: { rand_core:: OsRng , SaltString } ,
94+ Argon2 , PasswordHasher ,
95+ } ;
96+
97+ let salt = SaltString :: generate ( & mut OsRng ) ;
98+ let argon2 = Argon2 :: default ( ) ;
99+ let password_hash = argon2
100+ . hash_password ( password. as_bytes ( ) , & salt)
101+ . map_err ( |e| anyhow:: anyhow!( "Password hashing error: {}" , e) ) ?
102+ . to_string ( ) ;
103+
104+ match pool {
105+ DatabasePool :: Postgres ( pool) => {
106+ sqlx:: query (
107+ "INSERT INTO users (email, password_hash)
108+ VALUES ($1, $2)
109+ ON CONFLICT (email) DO NOTHING" ,
110+ )
111+ . bind ( email)
112+ . bind ( & password_hash)
113+ . execute ( pool)
114+ . await ?;
115+ }
116+ DatabasePool :: Sqlite ( pool) => {
117+ sqlx:: query (
118+ "INSERT OR IGNORE INTO users (email, password_hash)
119+ VALUES (?1, ?2)" ,
120+ )
121+ . bind ( email)
122+ . bind ( & password_hash)
123+ . execute ( pool)
124+ . await ?;
125+ }
126+ }
127+ info ! ( "Created admin user: {}" , email) ;
128+ Ok ( ( ) )
129+ }
130+
29131#[ actix_web:: main]
30132async fn main ( ) -> Result < ( ) > {
31133 // Load environment variables from .env file
0 commit comments