Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
medal
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
42
Issues
42
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Incidents
Environments
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
bwinf
medal
Commits
67b1307a
Commit
67b1307a
authored
Sep 30, 2020
by
Robert Czechowski
Committed by
Robert Czechowski
Oct 01, 2020
1
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Restart database connection if connection is lost
parent
d6bf1411
Pipeline
#850
passed with stages
in 18 minutes and 50 seconds
Changes
8
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
116 additions
and
43 deletions
+116
-43
src/core.rs
src/core.rs
+9
-8
src/db_conn.base.rs
src/db_conn.base.rs
+18
-11
src/db_conn.rs
src/db_conn.rs
+6
-1
src/db_conn_postgres.header.rs
src/db_conn_postgres.header.rs
+7
-0
src/db_conn_postgres.rs
src/db_conn_postgres.rs
+25
-11
src/db_conn_sqlite_new.header.rs
src/db_conn_sqlite_new.header.rs
+7
-0
src/db_conn_sqlite_new.rs
src/db_conn_sqlite_new.rs
+25
-11
src/webfw_iron.rs
src/webfw_iron.rs
+19
-1
No files found.
src/core.rs
View file @
67b1307a
...
...
@@ -46,13 +46,14 @@ pub struct ContestInfo {
pub
public
:
bool
,
}
#[derive(Clone)]
#[derive(Clone
,
Debug
)]
pub
enum
MedalError
{
NotLoggedIn
,
AccessDenied
,
CsrfCheckFailed
,
SessionTimeout
,
DatabaseError
,
DatabaseConnectionError
,
PasswordHashingError
,
UnmatchedPasswords
,
NotFound
,
...
...
@@ -189,7 +190,7 @@ pub fn debug<T: MedalConnection>(conn: &T, session_token: Option<String>)
pub
fn
debug_create_session
<
T
:
MedalConnection
>
(
conn
:
&
T
,
session_token
:
Option
<
String
>
)
{
if
let
Some
(
token
)
=
session_token
{
conn
.get_session_or_new
(
&
token
);
conn
.get_session_or_new
(
&
token
)
.unwrap
()
;
}
}
...
...
@@ -203,11 +204,11 @@ pub enum ContestVisibility {
pub
fn
show_contests
<
T
:
MedalConnection
>
(
conn
:
&
T
,
session_token
:
&
str
,
login_info
:
LoginInfo
,
visibility
:
ContestVisibility
)
->
MedalValue
->
MedalValue
Result
{
let
mut
data
=
json_val
::
Map
::
new
();
let
session
=
conn
.get_session_or_new
(
&
session_token
);
let
session
=
conn
.get_session_or_new
(
&
session_token
)
.map_err
(|
_
|
MedalError
::
DatabaseConnectionError
)
?
;
fill_user_data
(
&
session
,
&
mut
data
);
if
session
.is_logged_in
()
{
...
...
@@ -242,7 +243,7 @@ pub fn show_contests<T: MedalConnection>(conn: &T, session_token: &str, login_in
ContestVisibility
::
All
=>
"Alle Wettbewerbe"
,
}));
(
"contests"
.to_owned
(),
data
)
Ok
((
"contests"
.to_owned
(),
data
)
)
}
fn
generate_subtaskstars
(
tg
:
&
Taskgroup
,
grade
:
&
Grade
,
ast
:
Option
<
i32
>
)
->
Vec
<
SubTaskInfo
>
{
...
...
@@ -303,7 +304,7 @@ pub fn show_contest<T: MedalConnection>(conn: &T, contest_id: i32, session_token
query_string
:
Option
<
String
>
,
login_info
:
LoginInfo
)
->
MedalValueResult
{
let
session
=
conn
.get_session_or_new
(
&
session_token
);
let
session
=
conn
.get_session_or_new
(
&
session_token
)
.unwrap
()
;
let
contest
=
conn
.get_contest_by_id_complete
(
contest_id
);
let
grades
=
conn
.get_contest_user_grades
(
&
session_token
,
contest_id
);
...
...
@@ -485,7 +486,7 @@ pub fn show_contest_results<T: MedalConnection>(conn: &T, contest_id: i32, sessi
pub
fn
start_contest
<
T
:
MedalConnection
>
(
conn
:
&
T
,
contest_id
:
i32
,
session_token
:
&
str
,
csrf_token
:
&
str
)
->
MedalResult
<
()
>
{
// TODO: Is _or_new the right semantic? We need a CSRF token anyway …
let
session
=
conn
.get_session_or_new
(
&
session_token
);
let
session
=
conn
.get_session_or_new
(
&
session_token
)
.unwrap
()
;
let
contest
=
conn
.get_contest_by_id
(
contest_id
);
// Check logged in or open contest
...
...
@@ -680,7 +681,7 @@ pub fn save_submission<T: MedalConnection>(conn: &T, task_id: i32, session_token
}
pub
fn
show_task
<
T
:
MedalConnection
>
(
conn
:
&
T
,
task_id
:
i32
,
session_token
:
&
str
)
->
MedalValueResult
{
let
session
=
conn
.get_session_or_new
(
&
session_token
);
let
session
=
conn
.get_session_or_new
(
&
session_token
)
.unwrap
()
;
let
(
t
,
tg
,
c
)
=
conn
.get_task_by_id_complete
(
task_id
);
let
grade
=
conn
.get_taskgroup_user_grade
(
&
session_token
,
tg
.id
.unwrap
());
// TODO: Unwrap?
...
...
src/db_conn.base.rs
View file @
67b1307a
...
...
@@ -175,6 +175,8 @@ impl MedalObject<Connection> for Contest {
}
impl
MedalConnection
for
Connection
{
fn
reconnect
(
config
:
&
config
::
Config
)
->
Self
{
Self
::
reconnect_concrete
(
config
)
}
fn
dbtype
(
&
self
)
->
&
'static
str
{
"postgres"
}
fn
migration_already_applied
(
&
self
,
name
:
&
str
)
->
bool
{
...
...
@@ -308,16 +310,21 @@ impl MedalConnection for Connection {
SessionUser
::
minimal
(
id
,
session_token
.to_owned
(),
csrf_token
)
}
fn
get_session_or_new
(
&
self
,
key
:
&
str
)
->
SessionUser
{
let
query
=
"UPDATE session
SET session_token = $1
WHERE session_token = $2"
;
self
.get_session
(
&
key
)
.ensure_alive
()
.unwrap_or_else
(||
{
// TODO: Factor this out in own function
// TODO: Should a new session key be generated every time?
self
.execute
(
query
,
&
[
&
Option
::
<
String
>
::
None
,
&
key
])
.unwrap
();
self
.new_session
(
&
key
)
})
fn
get_session_or_new
(
&
self
,
key
:
&
str
)
->
Result
<
SessionUser
,
()
>
{
fn
disable_old_session_and_create_new
(
conn
:
&
Connection
,
key
:
&
str
)
->
Result
<
SessionUser
,
()
>
{
let
query
=
"UPDATE session
SET session_token = $1
WHERE session_token = $2"
;
// TODO: Should a new session key be generated every time?
conn
.execute
(
query
,
&
[
&
Option
::
<
String
>
::
None
,
&
key
])
.map_err
(|
_
|
())
?
;
Ok
(
conn
.new_session
(
&
key
))
}
if
let
Some
(
session
)
=
self
.get_session
(
&
key
)
.ensure_alive
()
{
Ok
(
session
)
}
else
{
disable_old_session_and_create_new
(
self
,
key
)
}
}
fn
get_user_by_id
(
&
self
,
user_id
:
i32
)
->
Option
<
SessionUser
>
{
...
...
@@ -571,7 +578,7 @@ impl MedalConnection for Connection {
fn
signup
(
&
self
,
session_token
:
&
str
,
username
:
&
str
,
email
:
&
str
,
password_hash
:
String
,
salt
:
&
str
)
->
SignupResult
{
let
mut
session_user
=
self
.get_session_or_new
(
&
session_token
);
let
mut
session_user
=
self
.get_session_or_new
(
&
session_token
)
.unwrap
()
;
if
session_user
.is_logged_in
()
{
return
SignupResult
::
UserLoggedIn
;
...
...
src/db_conn.rs
View file @
67b1307a
...
...
@@ -12,6 +12,7 @@
* You should have received a copy of the GNU Affero General Public License along with this program. If not, see *
\* <http://www.gnu.org/licenses/>. */
use
config
;
use
db_objects
::
*
;
#[derive(Debug)]
...
...
@@ -26,6 +27,8 @@ pub enum SignupResult {
/// This trait abstracts the database connection and provides function for all actions to be performed on the database
/// in the medal platform.
pub
trait
MedalConnection
{
fn
reconnect
(
&
config
::
Config
)
->
Self
;
fn
dbtype
(
&
self
)
->
&
'static
str
;
fn
migration_already_applied
(
&
self
,
name
:
&
str
)
->
bool
;
...
...
@@ -44,7 +47,9 @@ pub trait MedalConnection {
/// Saves the session data of `session` in the database.
fn
save_session
(
&
self
,
session
:
SessionUser
);
/// Combination of [`get_session`](#tymethod.get_session) and [`new_session`](#tymethod.new_session).
fn
get_session_or_new
(
&
self
,
key
:
&
str
)
->
SessionUser
;
///
/// This method can still fail in case of database error in order to bubble them up to the webframework
fn
get_session_or_new
(
&
self
,
key
:
&
str
)
->
Result
<
SessionUser
,
()
>
;
/// Try to get session associated to the id `user_id`.
///
...
...
src/db_conn_postgres.header.rs
View file @
67b1307a
...
...
@@ -16,6 +16,7 @@
extern
crate
postgres
;
use
config
;
use
postgres
::
Connection
;
use
time
;
use
time
::
Duration
;
...
...
@@ -34,6 +35,8 @@ trait Queryable {
where
F
:
FnMut
(
postgres
::
rows
::
Row
<
'_
>
)
->
T
;
fn
exists
(
&
self
,
sql
:
&
str
,
params
:
&
[
&
dyn
postgres
::
types
::
ToSql
])
->
bool
;
fn
get_last_id
(
&
self
)
->
Option
<
i32
>
;
fn
reconnect_concrete
(
&
config
::
Config
)
->
Self
;
}
impl
Queryable
for
Connection
{
...
...
@@ -62,6 +65,10 @@ impl Queryable for Connection {
})
}
// Empty line intended
fn
reconnect_concrete
(
config
:
&
config
::
Config
)
->
Self
{
postgres
::
Connection
::
connect
(
config
.database_url
.clone
()
.unwrap
(),
postgres
::
TlsMode
::
None
)
.unwrap
()
}
}
impl
MedalObject
<
Connection
>
for
Submission
{
...
...
src/db_conn_postgres.rs
View file @
67b1307a
...
...
@@ -29,6 +29,7 @@
extern
crate
postgres
;
use
config
;
use
postgres
::
Connection
;
use
time
;
use
time
::
Duration
;
...
...
@@ -47,6 +48,8 @@ trait Queryable {
where
F
:
FnMut
(
postgres
::
rows
::
Row
<
'_
>
)
->
T
;
fn
exists
(
&
self
,
sql
:
&
str
,
params
:
&
[
&
dyn
postgres
::
types
::
ToSql
])
->
bool
;
fn
get_last_id
(
&
self
)
->
Option
<
i32
>
;
fn
reconnect_concrete
(
&
config
::
Config
)
->
Self
;
}
impl
Queryable
for
Connection
{
...
...
@@ -75,6 +78,10 @@ impl Queryable for Connection {
})
}
// Empty line intended
fn
reconnect_concrete
(
config
:
&
config
::
Config
)
->
Self
{
postgres
::
Connection
::
connect
(
config
.database_url
.clone
()
.unwrap
(),
postgres
::
TlsMode
::
None
)
.unwrap
()
}
}
impl
MedalObject
<
Connection
>
for
Submission
{
...
...
@@ -287,6 +294,8 @@ impl MedalObject<Connection> for Contest {
}
impl
MedalConnection
for
Connection
{
fn
reconnect
(
config
:
&
config
::
Config
)
->
Self
{
Self
::
reconnect_concrete
(
config
)
}
fn
dbtype
(
&
self
)
->
&
'static
str
{
"postgres"
}
fn
migration_already_applied
(
&
self
,
name
:
&
str
)
->
bool
{
...
...
@@ -420,16 +429,21 @@ impl MedalConnection for Connection {
SessionUser
::
minimal
(
id
,
session_token
.to_owned
(),
csrf_token
)
}
fn
get_session_or_new
(
&
self
,
key
:
&
str
)
->
SessionUser
{
let
query
=
"UPDATE session
SET session_token = $1
WHERE session_token = $2"
;
self
.get_session
(
&
key
)
.ensure_alive
()
.unwrap_or_else
(||
{
// TODO: Factor this out in own function
// TODO: Should a new session key be generated every time?
self
.execute
(
query
,
&
[
&
Option
::
<
String
>
::
None
,
&
key
])
.unwrap
();
self
.new_session
(
&
key
)
})
fn
get_session_or_new
(
&
self
,
key
:
&
str
)
->
Result
<
SessionUser
,
()
>
{
fn
disable_old_session_and_create_new
(
conn
:
&
Connection
,
key
:
&
str
)
->
Result
<
SessionUser
,
()
>
{
let
query
=
"UPDATE session
SET session_token = $1
WHERE session_token = $2"
;
// TODO: Should a new session key be generated every time?
conn
.execute
(
query
,
&
[
&
Option
::
<
String
>
::
None
,
&
key
])
.map_err
(|
_
|
())
?
;
Ok
(
conn
.new_session
(
&
key
))
}
if
let
Some
(
session
)
=
self
.get_session
(
&
key
)
.ensure_alive
()
{
Ok
(
session
)
}
else
{
disable_old_session_and_create_new
(
self
,
key
)
}
}
fn
get_user_by_id
(
&
self
,
user_id
:
i32
)
->
Option
<
SessionUser
>
{
...
...
@@ -683,7 +697,7 @@ impl MedalConnection for Connection {
fn
signup
(
&
self
,
session_token
:
&
str
,
username
:
&
str
,
email
:
&
str
,
password_hash
:
String
,
salt
:
&
str
)
->
SignupResult
{
let
mut
session_user
=
self
.get_session_or_new
(
&
session_token
);
let
mut
session_user
=
self
.get_session_or_new
(
&
session_token
)
.unwrap
()
;
if
session_user
.is_logged_in
()
{
return
SignupResult
::
UserLoggedIn
;
...
...
src/db_conn_sqlite_new.header.rs
View file @
67b1307a
...
...
@@ -16,6 +16,7 @@
extern
crate
rusqlite
;
use
config
;
use
rusqlite
::
Connection
;
use
time
;
use
time
::
Duration
;
...
...
@@ -34,6 +35,8 @@ trait Queryable {
where
F
:
FnMut
(
&
rusqlite
::
Row
)
->
T
;
fn
exists
(
&
self
,
sql
:
&
str
,
params
:
&
[
&
dyn
rusqlite
::
types
::
ToSql
])
->
bool
;
fn
get_last_id
(
&
self
)
->
Option
<
i32
>
;
fn
reconnect_concrete
(
&
config
::
Config
)
->
Self
;
}
impl
Queryable
for
Connection
{
...
...
@@ -62,6 +65,10 @@ impl Queryable for Connection {
}
fn
get_last_id
(
&
self
)
->
Option
<
i32
>
{
self
.query_row
(
"SELECT last_insert_rowid()"
,
&
[],
|
row
|
row
.get
(
0
))
.ok
()
}
fn
reconnect_concrete
(
config
:
&
config
::
Config
)
->
Self
{
rusqlite
::
Connection
::
open
(
config
.database_file
.clone
()
.unwrap
())
.unwrap
()
}
}
impl
MedalObject
<
Connection
>
for
Submission
{
...
...
src/db_conn_sqlite_new.rs
View file @
67b1307a
...
...
@@ -29,6 +29,7 @@
extern
crate
rusqlite
;
use
config
;
use
rusqlite
::
Connection
;
use
time
;
use
time
::
Duration
;
...
...
@@ -47,6 +48,8 @@ trait Queryable {
where
F
:
FnMut
(
&
rusqlite
::
Row
)
->
T
;
fn
exists
(
&
self
,
sql
:
&
str
,
params
:
&
[
&
dyn
rusqlite
::
types
::
ToSql
])
->
bool
;
fn
get_last_id
(
&
self
)
->
Option
<
i32
>
;
fn
reconnect_concrete
(
&
config
::
Config
)
->
Self
;
}
impl
Queryable
for
Connection
{
...
...
@@ -75,6 +78,10 @@ impl Queryable for Connection {
}
fn
get_last_id
(
&
self
)
->
Option
<
i32
>
{
self
.query_row
(
"SELECT last_insert_rowid()"
,
&
[],
|
row
|
row
.get
(
0
))
.ok
()
}
fn
reconnect_concrete
(
config
:
&
config
::
Config
)
->
Self
{
rusqlite
::
Connection
::
open
(
config
.database_file
.clone
()
.unwrap
())
.unwrap
()
}
}
impl
MedalObject
<
Connection
>
for
Submission
{
...
...
@@ -287,6 +294,8 @@ impl MedalObject<Connection> for Contest {
}
impl
MedalConnection
for
Connection
{
fn
reconnect
(
config
:
&
config
::
Config
)
->
Self
{
Self
::
reconnect_concrete
(
config
)
}
fn
dbtype
(
&
self
)
->
&
'static
str
{
"sqlite_v2"
}
fn
migration_already_applied
(
&
self
,
name
:
&
str
)
->
bool
{
...
...
@@ -420,16 +429,21 @@ impl MedalConnection for Connection {
SessionUser
::
minimal
(
id
,
session_token
.to_owned
(),
csrf_token
)
}
fn
get_session_or_new
(
&
self
,
key
:
&
str
)
->
SessionUser
{
let
query
=
"UPDATE session
SET session_token = ?1
WHERE session_token = ?2"
;
self
.get_session
(
&
key
)
.ensure_alive
()
.unwrap_or_else
(||
{
// TODO: Factor this out in own function
// TODO: Should a new session key be generated every time?
self
.execute
(
query
,
&
[
&
Option
::
<
String
>
::
None
,
&
key
])
.unwrap
();
self
.new_session
(
&
key
)
})
fn
get_session_or_new
(
&
self
,
key
:
&
str
)
->
Result
<
SessionUser
,
()
>
{
fn
disable_old_session_and_create_new
(
conn
:
&
Connection
,
key
:
&
str
)
->
Result
<
SessionUser
,
()
>
{
let
query
=
"UPDATE session
SET session_token = ?1
WHERE session_token = ?2"
;
// TODO: Should a new session key be generated every time?
conn
.execute
(
query
,
&
[
&
Option
::
<
String
>
::
None
,
&
key
])
.map_err
(|
_
|
())
?
;
Ok
(
conn
.new_session
(
&
key
))
}
if
let
Some
(
session
)
=
self
.get_session
(
&
key
)
.ensure_alive
()
{
Ok
(
session
)
}
else
{
disable_old_session_and_create_new
(
self
,
key
)
}
}
fn
get_user_by_id
(
&
self
,
user_id
:
i32
)
->
Option
<
SessionUser
>
{
...
...
@@ -683,7 +697,7 @@ impl MedalConnection for Connection {
fn
signup
(
&
self
,
session_token
:
&
str
,
username
:
&
str
,
email
:
&
str
,
password_hash
:
String
,
salt
:
&
str
)
->
SignupResult
{
let
mut
session_user
=
self
.get_session_or_new
(
&
session_token
);
let
mut
session_user
=
self
.get_session_or_new
(
&
session_token
)
.unwrap
()
;
if
session_user
.is_logged_in
()
{
return
SignupResult
::
UserLoggedIn
;
...
...
src/webfw_iron.rs
View file @
67b1307a
...
...
@@ -272,6 +272,10 @@ impl<'c, 'a, 'b> From<AugMedalError<'c, 'a, 'b>> for IronError {
IronError
{
error
:
Box
::
new
(
SessionError
{
message
:
"Database Error"
.to_string
()
}),
response
:
Response
::
with
(
status
::
InternalServerError
)
}
}
core
::
MedalError
::
DatabaseConnectionError
=>
{
IronError
{
error
:
Box
::
new
(
SessionError
{
message
:
"Database Connection Error"
.to_string
()
}),
response
:
Response
::
with
(
status
::
InternalServerError
)
}
}
core
::
MedalError
::
PasswordHashingError
=>
{
IronError
{
error
:
Box
::
new
(
SessionError
{
message
:
"Error hashing the passwords"
.to_string
()
}),
response
:
Response
::
with
(
status
::
InternalServerError
)
}
...
...
@@ -393,7 +397,21 @@ fn contests<C>(req: &mut Request) -> IronResult<Response>
};
let
config
=
req
.get
::
<
Read
<
SharedConfiguration
>>
()
.unwrap
();
let
(
template
,
mut
data
)
=
with_conn!
[
core
::
show_contests
,
C
,
req
,
&
session_token
,
login_info
(
&
config
),
visibility
];
let
res
=
with_conn!
[
core
::
show_contests
,
C
,
req
,
&
session_token
,
login_info
(
&
config
),
visibility
];
if
res
.is_err
()
{
// Database connection failed … Create a new database connection!
// TODO: This code should be unified with the database creation code in main.rs
println!
(
"DATABASE CONNECTION LOST! Restarting database connection."
);
let
conn
=
C
::
reconnect
(
&
config
);
let
mutex
=
req
.get
::
<
Write
<
SharedDatabaseConnection
<
C
>>>
()
.unwrap
();
let
mut
sharedconn
=
mutex
.lock
()
.unwrap_or_else
(|
e
|
e
.into_inner
());
*
sharedconn
=
conn
;
// return ServerError();
}
let
(
template
,
mut
data
)
=
res
.unwrap
();
data
.insert
(
"config"
.to_string
(),
to_json
(
&
config
.template_params
));
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment