Updated more UI. Enhanced login capabilities. Started work on querying data.

This commit is contained in:
Gnarwhal 2021-02-07 22:50:48 -05:00
parent 40a0e4046a
commit 052052d76b
Signed by: Gnarwhal
GPG key ID: 0989A73D8C421174
29 changed files with 706 additions and 424 deletions

View file

@ -1,65 +0,0 @@
package achievements.controllers;
import achievements.data.Achievements;
import achievements.data.Games;
import achievements.data.InternalError;
import achievements.services.DbService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import static org.springframework.web.bind.annotation.RequestMethod.GET;
@RestController
public class Controller {
@Autowired
private DbService db;
public Controller() {}
@RequestMapping(value = { "/achievements", "/achievements/{Name}" }, method = GET, produces = "application/json")
public ResponseEntity<String> fetchAchievements(@PathVariable(value = "Name", required = false) String getName) {
var achievements = (Achievements) null;
if (getName == null) {
achievements = db.getAchievements("%");
} else {
achievements = db.getAchievements(getName);
}
var mapper = new ObjectMapper();
try {
if (achievements == null) {
return new ResponseEntity(mapper.writeValueAsString(new InternalError("Could not get achievements from database")), HttpStatus.INTERNAL_SERVER_ERROR);
} else {
return new ResponseEntity(mapper.writeValueAsString(achievements), HttpStatus.OK);
}
} catch (JsonProcessingException e) {
e.printStackTrace();
return new ResponseEntity("{}", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@RequestMapping(value = { "/games", "/games/{Name}" }, method = GET, produces = "application/json")
public ResponseEntity<String> fetchGames(@PathVariable(value = "Name", required = false) String getName) {
var games = (Games) null;
if (getName == null) {
games = db.getGames("%");
} else {
games = db.getGames(getName);
}
var mapper = new ObjectMapper();
try {
if (games == null) {
return new ResponseEntity(mapper.writeValueAsString(new InternalError("Could not get games from database")), HttpStatus.INTERNAL_SERVER_ERROR);
} else {
return new ResponseEntity(mapper.writeValueAsString(games), HttpStatus.OK);
}
} catch (JsonProcessingException e) {
e.printStackTrace();
return new ResponseEntity("{}", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}

View file

@ -0,0 +1,11 @@
package achievements.controllers;
import achievements.data.Profile;
import org.springframework.web.bind.annotation.RequestBody;
public class DataController {
public void getProfile(@RequestBody Profile.Query query) {
}
}

View file

@ -1,5 +1,7 @@
package achievements.controllers; package achievements.controllers;
import achievements.data.APError;
import achievements.data.Session;
import achievements.data.User; import achievements.data.User;
import achievements.services.AuthenticationService; import achievements.services.AuthenticationService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -7,7 +9,6 @@ import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import static org.springframework.web.bind.annotation.RequestMethod.POST; import static org.springframework.web.bind.annotation.RequestMethod.POST;
@ -29,11 +30,17 @@ public class LoginController {
public ResponseEntity createUser(@RequestBody User user) { public ResponseEntity createUser(@RequestBody User user) {
var response = authService.createUser(user); var response = authService.createUser(user);
if (response.status == 0) { if (response.status == 0) {
return ResponseEntity.ok("{ \"key\": \"" + authService.session().generate(response.id) + "\", \"id\": " + response.id + " }"); return ResponseEntity.ok(
new Session(
authService.session().generate(response.id),
response.id,
response.hue
)
);
} else if (response.status > 0) { } else if (response.status > 0) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("{ \"code\": " + response.status + " }"); return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(new APError(response.status));
} else { } else {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("{ \"code\": " + response.status + " }"); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new APError(response.status));
} }
} }
@ -50,17 +57,27 @@ public class LoginController {
* -1 => Unknown error * -1 => Unknown error
*/ */
@RequestMapping(value = "/login", method = POST, consumes = "application/json", produces = "application/json") @RequestMapping(value = "/login", method = POST, consumes = "application/json", produces = "application/json")
public ResponseEntity login(@RequestParam(value = "guest", required = false) boolean guest, @RequestBody User user) { public ResponseEntity login(@RequestBody User user) {
var response = guest ? var response = authService.login(user);
authService.GUEST :
authService.login(user);
if (response.status == 0) { if (response.status == 0) {
return ResponseEntity.ok("{ \"key\": \"" + authService.session().generate(response.id) + "\", \"id\": " + response.id + " }"); return ResponseEntity.ok(
new Session(
authService.session().generate(response.id),
response.id,
response.hue
)
);
} else if (response.status > 0) { } else if (response.status > 0) {
// Hardcoded 1 response code // Hardcoded 1 response code
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("{ \"code\": 1 }"); return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(new APError(1));
} else { } else {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("{ \"code\": " + response.status + " }"); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new APError(response.status));
} }
} }
@RequestMapping(value = "/logout", method = POST, consumes = "application/json", produces = "application/json")
public ResponseEntity logout(@RequestBody Session session) {
authService.logout(session);
return ResponseEntity.ok("{}");
}
} }

View file

@ -0,0 +1,36 @@
package achievements.data;
import com.fasterxml.jackson.annotation.JsonProperty;
public class APError {
@JsonProperty("code")
private int code;
@JsonProperty("message")
private String message;
public APError(int code) {
this.code = code;
}
public APError(int code, String message) {
this.code = code;
this.message = message;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}

View file

@ -0,0 +1,157 @@
package achievements.data;
import achievements.data.query.NumericFilter;
import achievements.data.query.StringFilter;
import com.fasterxml.jackson.annotation.JsonProperty;
public class Achievement {
public static class Query {
@JsonProperty("sessionKey")
private String sessionKey;
@JsonProperty("name")
private StringFilter name;
@JsonProperty("stages")
private NumericFilter stages;
@JsonProperty("completion")
private NumericFilter completion;
@JsonProperty("difficulty")
private NumericFilter difficulty;
@JsonProperty("quality")
private NumericFilter quality;
public Query(String sessionKey, StringFilter name, NumericFilter stages, NumericFilter completion, NumericFilter difficulty, NumericFilter quality) {
this.sessionKey = sessionKey;
this.name = name;
this.stages = stages;
this.completion = completion;
this.difficulty = difficulty;
this.quality = quality;
}
public String getSessionKey() {
return sessionKey;
}
public void setSessionKey(String sessionKey) {
this.sessionKey = sessionKey;
}
public StringFilter getName() {
return name;
}
public void setName(StringFilter name) {
this.name = name;
}
public NumericFilter getStages() {
return stages;
}
public void setStages(NumericFilter stages) {
this.stages = stages;
}
public NumericFilter getCompletion() {
return completion;
}
public void setCompletion(NumericFilter completion) {
this.completion = completion;
}
public NumericFilter getDifficulty() {
return difficulty;
}
public void setDifficulty(NumericFilter difficulty) {
this.difficulty = difficulty;
}
public NumericFilter getQuality() {
return quality;
}
public void setQuality(NumericFilter quality) {
this.quality = quality;
}
}
@JsonProperty("ID")
private int id;
@JsonProperty("game")
private int gameId;
@JsonProperty("name")
private String name;
@JsonProperty("description")
private String description;
@JsonProperty("stages")
private int stages;
@JsonProperty("completion")
private float completion;
@JsonProperty("difficulty")
private float difficulty;
@JsonProperty("quality")
private float quality;
public Achievement(int id, int gameId, String name, String description, int stages, float completion, float difficulty, float quality) {
this.id = id;
this.gameId = gameId;
this.name = name;
this.description = description;
this.stages = stages;
this.completion = completion;
this.difficulty = difficulty;
this.quality = quality;
}
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public int getGameId() {
return gameId;
}
public void setGameId(int gameId) {
this.gameId = gameId;
}
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public int getStages() { return stages; }
public void setStages(int stages) { this.stages = stages; }
public float getCompletion() {
return completion;
}
public void setCompletion(float completion) {
this.completion = completion;
}
public float getDifficulty() {
return difficulty;
}
public void setDifficulty(float difficulty) {
this.difficulty = difficulty;
}
public float getQuality() {
return quality;
}
public void setQuality(float quality) {
this.quality = quality;
}
}

View file

@ -1,64 +0,0 @@
package achievements.data;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.ArrayList;
import java.util.List;
public class Achievements {
public static class Achievement {
@JsonProperty("name")
private String name;
@JsonProperty("description")
private String description;
@JsonProperty("stages")
private int stages;
public Achievement(String name, String description, int stages) {
this.name = name;
this.description = description;
this.stages = stages;
}
// Start Getters/Setters
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public int getStages() { return stages; }
public void setStages(int stages) { this.stages = stages; }
// End Getters/Setters
}
@JsonProperty("gameID")
private int gameID;
@JsonProperty("gameName")
private String gameName;
@JsonProperty("achievements")
private List<Achievement> achievements;
public Achievements() { achievements = new ArrayList<Achievement>(); }
// Start Getters/Setters
public int getGameID() { return gameID; }
public void setGameID(int gameID) { this.gameID = gameID; }
public String getGameName() { return gameName; }
public void setGameName(String gameName) { this.gameName = gameName; }
public List<Achievement> getAchievements() { return achievements; }
public void setAchievements(List<Achievement> achievements) { this.achievements = achievements; }
// End Getters/Setters
public void addAchievement(Achievement achievement) { this.achievements.add(achievement); };
}

View file

@ -0,0 +1,47 @@
package achievements.data;
import achievements.data.query.StringFilter;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.ArrayList;
import java.util.List;
public class Game {
public static class Query {
@JsonProperty("name")
private StringFilter name;
@JsonProperty("platforms")
private StringFilter platforms;
}
@JsonProperty("ID")
private int id;
@JsonProperty("name")
private String name;
@JsonProperty("platforms")
private List<String> platforms;
@JsonProperty("achievementCount")
private int achievementCount;
public Game(int id, String name, String platform) {
this.id = id;
this.name = name;
this.platforms = new ArrayList<>();
this.platforms.add(platform);
}
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public List<String> getPlatforms() { return platforms; }
public void setPlatforms(List<String> platforms) { this.platforms = platforms; }
public void addToPlatforms(String platform) { this.platforms.add(platform); }
}

View file

@ -1,57 +0,0 @@
package achievements.data;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.ArrayList;
import java.util.List;
public class Games {
public static class Game {
@JsonProperty("ID")
private int id;
@JsonProperty("name")
private String name;
@JsonProperty("platforms")
private List<String> platforms;
public Game(int id, String name, String platform) {
this.id = id;
this.name = name;
this.platforms = new ArrayList<>();
this.platforms.add(platform);
}
// Start Getters/Setters
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public List<String> getPlatforms() { return platforms; }
public void setPlatforms(List<String> platforms) { this.platforms = platforms; }
public void addToPlatforms(String platform) { this.platforms.add(platform); }
// End Getters/Setters
}
@JsonProperty("games")
private List<Game> games;
public Games() { games = new ArrayList<Game>(); }
// Start Getters/Setters
public List<Game> getGames() { return games; }
public void setGames(List<Game> games) { this.games = games; }
// End Getters/Setters
public void addGame(Game game) { this.games.add(game); }
}

View file

@ -1,21 +0,0 @@
package achievements.data;
import com.fasterxml.jackson.annotation.JsonProperty;
public class InternalError {
@JsonProperty
private String message;
public InternalError(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}

View file

@ -0,0 +1,73 @@
package achievements.data;
import achievements.data.query.StringFilter;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List;
public class Profile {
public static class Query {
@JsonProperty("username")
private StringFilter string;
}
@JsonProperty("id")
private int id;
@JsonProperty("username")
private String username;
@JsonProperty("plaforms")
private List<String> platforms;
@JsonProperty("games")
private List<Game> games;
@JsonProperty("achievements")
private List<Achievement> achievements;
public Profile(int id, String username, List<String> platforms, List<Game> games, List<Achievement> achievements) {
this.id = id;
this.username = username;
this.platforms = platforms;
this.games = games;
this.achievements = achievements;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public List<String> getPlatforms() {
return platforms;
}
public void setPlatforms(List<String> platforms) {
this.platforms = platforms;
}
public List<Game> getGames() {
return games;
}
public void setGames(List<Game> games) {
this.games = games;
}
public List<Achievement> getAchievements() {
return achievements;
}
public void setAchievements(List<Achievement> achievements) {
this.achievements = achievements;
}
}

View file

@ -0,0 +1,43 @@
package achievements.data;
import com.fasterxml.jackson.annotation.JsonProperty;
public class Session {
@JsonProperty("key")
private String key;
@JsonProperty("id")
private int id;
@JsonProperty("hue")
private int hue;
public Session(String key, int id, int hue) {
this.key = key;
this.id = id;
this.hue = hue;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getHue() {
return hue;
}
public void setHue(int hue) {
this.hue = hue;
}
}

View file

@ -0,0 +1,32 @@
package achievements.data.query;
import com.fasterxml.jackson.annotation.JsonProperty;
public class NumericFilter {
@JsonProperty("min")
private Float min;
@JsonProperty("max")
private Float max;
public NumericFilter(Float min, Float max) {
this.min = min;
this.max = max;
}
public Float getMin() {
return min;
}
public void setMin(Float min) {
this.min = min;
}
public Float getMax() {
return max;
}
public void setMax(Float max) {
this.max = max;
}
}

View file

@ -0,0 +1,21 @@
package achievements.data.query;
import com.fasterxml.jackson.annotation.JsonProperty;
public class StringFilter {
@JsonProperty("query")
private String query;
public StringFilter(String query) {
this.query = query;
}
public String getQuery() {
return query;
}
public void setQuery(String query) {
this.query = query;
}
}

View file

@ -16,13 +16,11 @@ public class SessionManager {
return key; return key;
} }
public String guest() { public int getUser(String key) {
var key = HashManager.encode(HashManager.generateBytes(16));
session.put(key, null);
return key;
}
public Integer getUser(String key) {
return session.get(key); return session.get(key);
} }
public void remove(String key) {
session.remove(key);
}
} }

View file

@ -1,5 +1,6 @@
package achievements.services; package achievements.services;
import achievements.data.Session;
import achievements.data.User; import achievements.data.User;
import achievements.misc.DbConnectionService; import achievements.misc.DbConnectionService;
import achievements.misc.Password; import achievements.misc.Password;
@ -16,10 +17,12 @@ public class AuthenticationService {
public static class LoginResponse { public static class LoginResponse {
public int status; public int status;
public Integer id; public Integer id;
public int hue;
public LoginResponse() { public LoginResponse() {
this.status = 0; this.status = 0;
this.id = null; this.id = null;
this.hue = 0;
} }
public LoginResponse(int status) { public LoginResponse(int status) {
@ -27,14 +30,13 @@ public class AuthenticationService {
this.id = null; this.id = null;
} }
public LoginResponse(int status, int id) { public LoginResponse(int status, int id, int hue) {
this.status = status; this.status = status;
this.id = id; this.id = id;
this.hue = hue;
} }
} }
public static final LoginResponse GUEST = new LoginResponse();
@Autowired @Autowired
private DbConnectionService dbs; private DbConnectionService dbs;
private Connection db; private Connection db;
@ -53,7 +55,7 @@ public class AuthenticationService {
} }
try { try {
var statement = db.prepareCall("{? = call CreateUser(?, ?, ?, ?, ?)}"); var statement = db.prepareCall("{? = call CreateUser(?, ?, ?, ?, ?, ?)}");
statement.registerOutParameter(1, Types.INTEGER); statement.registerOutParameter(1, Types.INTEGER);
statement.setString(2, user.getEmail()); statement.setString(2, user.getEmail());
statement.setString(3, user.getUsername()); statement.setString(3, user.getUsername());
@ -63,9 +65,14 @@ public class AuthenticationService {
statement.setString(5, password.hash); statement.setString(5, password.hash);
statement.registerOutParameter(6, Types.INTEGER); statement.registerOutParameter(6, Types.INTEGER);
statement.registerOutParameter(7, Types.INTEGER);
statement.execute(); statement.execute();
var response = new LoginResponse(statement.getInt(1), statement.getInt(6)); var response = new LoginResponse(
statement.getInt(1),
statement.getInt(6),
statement.getInt(7)
);
statement.close(); statement.close();
return response; return response;
@ -86,7 +93,7 @@ public class AuthenticationService {
var salt = result.getString("Salt"); var salt = result.getString("Salt");
var hash = result.getString("Password"); var hash = result.getString("Password");
if (Password.validate(salt, user.getPassword(), hash)) { if (Password.validate(salt, user.getPassword(), hash)) {
response = new LoginResponse(0, result.getInt("ID")); response = new LoginResponse(0, result.getInt("ID"), result.getInt("Hue"));
} else { } else {
response = new LoginResponse(2); response = new LoginResponse(2);
} }
@ -100,6 +107,10 @@ public class AuthenticationService {
return response; return response;
} }
public void logout(Session key) {
session.remove(key.getKey());
}
public SessionManager session() { public SessionManager session() {
return session; return session;
} }

View file

@ -0,0 +1,40 @@
package achievements.services;
import achievements.data.Achievement;
import achievements.data.Game;
import achievements.data.Profile;
import achievements.misc.DbConnectionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.sql.Connection;
import java.util.List;
@Service
public class DataService {
@Autowired
private DbConnectionService dbs;
private Connection db;
@Autowired
private AuthenticationService auth;
@PostConstruct
private void init() {
db = dbs.getConnection();
}
/*public List<Achievement> getUsers() {
}
public List<Game> getGames() {
}
public List<Profile> getProfiles() {
}*/
}

View file

@ -1,92 +0,0 @@
package achievements.services;
import achievements.data.Achievements;
import achievements.data.Games;
import achievements.misc.DbConnectionService;
import achievements.misc.SessionManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.sql.*;
@Service
public class DbService {
@Autowired
private DbConnectionService dbs;
private Connection db;
@PostConstruct
private void init() {
db = dbs.getConnection();
}
public Achievements getAchievements(String gameName) {
try {
// Create Query
CallableStatement stmt = db.prepareCall("{? = call GetAchievements(?)}");
stmt.registerOutParameter(1, Types.INTEGER);
stmt.setString(2, gameName);
// Read Result(s)
ResultSet results = stmt.executeQuery();
var achievements = new Achievements();
while (results.next()) {
// Add Result(s) to data class
int achievementGameID = results.getInt("GameID");
String achievementGameName = results.getString("GameName");
String achievementName = results.getString("Name");
String achievementDescription = results.getString("Description");
int achievementStages = results.getInt("Stages");
// Checks if getting from specific game or all achievements
if (!gameName.equals("%")) {
achievements.setGameID(achievementGameID);
achievements.setGameName(achievementGameName);
}
achievements.addAchievement(new Achievements.Achievement(achievementName, achievementDescription, achievementStages));
}
stmt.close();
return achievements;
} catch (SQLException e) {
e.printStackTrace();
return null;
}
}
public Games getGames(String name) {
try {
// Create Query
CallableStatement stmt = db.prepareCall("{? = call GetGame(?)}");
stmt.registerOutParameter(1, Types.INTEGER);
stmt.setString(2, name);
// Read Result(s)
ResultSet results = stmt.executeQuery();
var games = new Games();
while (results.next()) {
// Add Result(s) to data class
int gameID = results.getInt("ID");
String gameName = results.getString("Name");
String gamePlatform = results.getString("PlatformName");
if (!games.getGames().isEmpty()) {
var lastGame = games.getGames().get(games.getGames().size()-1);
if (lastGame.getId() == gameID) {
lastGame.addToPlatforms(gamePlatform);
} else {
games.addGame(new Games.Game(gameID,gameName,gamePlatform));
}
} else {
games.addGame(new Games.Game(gameID,gameName,gamePlatform));
}
}
stmt.close();
return games;
} catch (SQLException e) {
e.printStackTrace();
return null;
}
}
}

View file

@ -9,14 +9,18 @@
</head> </head>
<body> <body>
<div id="navbar"></div> <div id="navbar"></div>
<div id="content-body"> <div id="content-body">
<div id="login-page" class="page"> <div id="login-page" class="page">
<div class="page-subsection">
<div class="page-header"> <div class="page-header">
<p class="page-header-text">Achievements Project</p> <p class="page-header-text">Achievements Project</p>
<div class="page-header-separator"></div> <div class="page-header-separator"></div>
</div> </div>
<div id="login-header"> </div>
<div id="login-flex">
<div id="login-subsection" class="page-subsection">
<div id="login-elements" class="page-subsection-wrapper">
<div id="login-header" class="page-subheader">
<p id="login-header-text" class="page-subheader-text">Login</p> <p id="login-header-text" class="page-subheader-text">Login</p>
<div class="page-subheader-separator"></div> <div class="page-subheader-separator"></div>
</div> </div>
@ -31,7 +35,11 @@
<div id="guest-login-button" class="ap-button login">Continue as Guest</div> <div id="guest-login-button" class="ap-button login">Continue as Guest</div>
</div> </div>
<div id="login-button" class="ap-button login form-row">Login</div> <div id="login-button" class="ap-button login form-row">Login</div>
<p id="warning" class="form-row multiline">WARNING! The security of this project is questionable at best. Please refrain from using any truly sensitive data.</p> <p id="warning" class="form-row">WARNING!</p>
<p id="warning-message" class="form-row multiline">The security of this project is questionable at best. Please refrain from using any truly sensitive data.</p>
</div>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>

View file

@ -1,3 +1,11 @@
let session = null;
const loadSession = () => {
session = JSON.parse(window.sessionStorage.getItem('session'));
if (session) {
document.querySelector(":root").style.setProperty('--accent-hue', session.hue);
}
};
const expandTemplates = async () => { const expandTemplates = async () => {
template.apply("navbar").values([ template.apply("navbar").values([
{ section: "left" }, { section: "left" },
@ -7,9 +15,16 @@ const expandTemplates = async () => {
{ item: "games", title: "Games" }, { item: "games", title: "Games" },
{ item: "achievements", title: "Achievements" } { item: "achievements", title: "Achievements" }
]); ]);
if (session) {
template.apply("navbar-section-right").values([ template.apply("navbar-section-right").values([
{ item: "profile", title: "Profile" } { item: "profile", title: "Profile" },
{ item: "logout", title: "Logout" }
]); ]);
} else {
template.apply("navbar-section-right").values([
{ item: "login", title: "Login" }
]);
}
template.apply("content-body").values([ template.apply("content-body").values([
{ page: "games", title: "Games" }, { page: "games", title: "Games" },
{ page: "achievements", title: "Achievements" }, { page: "achievements", title: "Achievements" },
@ -18,7 +33,6 @@ const expandTemplates = async () => {
template.apply("extern-games-page" ).values("games_page" ); template.apply("extern-games-page" ).values("games_page" );
template.apply("extern-achievements-page").values("achievements_page"); template.apply("extern-achievements-page").values("achievements_page");
template.apply("extern-profile-page" ).values("profile_page" ); template.apply("extern-profile-page" ).values("profile_page" );
template.apply("achievements-page-list" ).fetch("achievements", "https://localhost:4730/achievements");
await template.expand(); await template.expand();
}; };
@ -32,6 +46,24 @@ const connectNavbar = () => {
const navItems = document.querySelectorAll(".navbar-item"); const navItems = document.querySelectorAll(".navbar-item");
for (const item of navItems) { for (const item of navItems) {
if (item.dataset.pageName === "logout") {
item.addEventListener("click", (clickEvent) => {
fetch('https://localhost:4730/logout', {
method: 'POST',
mode: 'cors',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ key: session.key })
})
.then(response => {
window.sessionStorage.removeItem('session');
window.location.href = "/login.html";
});
});
} else if (item.dataset.pageName === "login") {
item.addEventListener("click", (clickEvent) => window.location.href = "/login.html");
} else {
item.addEventListener("click", (clickEvent) => { item.addEventListener("click", (clickEvent) => {
const navItemPageId = item.dataset.pageName + "-page" const navItemPageId = item.dataset.pageName + "-page"
for (const page of pages) { for (const page of pages) {
@ -43,11 +75,12 @@ const connectNavbar = () => {
} }
}); });
} }
}
}; };
const connectProfile = () => { const connectProfile = () => {
const games = document.querySelector("#profile-games"); const games = document.querySelector("#profile-games-header");
const achievements = document.querySelector("#profile-achievements"); const achievements = document.querySelector("#profile-achievements-header");
games.children[0].addEventListener("click", (clickEvent) => { games.children[0].addEventListener("click", (clickEvent) => {
for (const page of pages) { for (const page of pages) {
@ -84,6 +117,8 @@ const loadFilters = () => {
} }
window.addEventListener("load", async (loadEvent) => { window.addEventListener("load", async (loadEvent) => {
loadSession();
await expandTemplates(); await expandTemplates();
loadPages(); loadPages();

View file

@ -1,4 +1,9 @@
window.addEventListener("load", (loadEvent) => { window.addEventListener("load", (loadEvent) => {
let session = window.sessionStorage.getItem('session');
if (session) {
window.location.href = '/';
}
const fields = { const fields = {
email: document.querySelector("#email" ), email: document.querySelector("#email" ),
username: document.querySelector("#username"), username: document.querySelector("#username"),
@ -27,6 +32,18 @@ window.addEventListener("load", (loadEvent) => {
} }
let frozen = false; let frozen = false;
const freeze = () => {
frozen = true;
createUser.classList.add("disabled");
login.classList.add("disabled");
guest.classList.add("disabled");
};
const unfreeze = () => {
frozen = false;
createUser.classList.remove("disabled");
login.classList.remove("disabled");
guest.classList.remove("disabled");
};
const switchToCreateAction = (clickEvent) => { const switchToCreateAction = (clickEvent) => {
if (!frozen) { if (!frozen) {
@ -39,6 +56,8 @@ window.addEventListener("load", (loadEvent) => {
login.removeEventListener("click", loginAction); login.removeEventListener("click", loginAction);
login.addEventListener("click", switchToLoginAction); login.addEventListener("click", switchToLoginAction);
activeAction = createUserAction;
} }
}; };
const createUserAction = (clickEvent) => { const createUserAction = (clickEvent) => {
@ -52,7 +71,7 @@ window.addEventListener("load", (loadEvent) => {
} else if (fields.password.value === '') { } else if (fields.password.value === '') {
raiseError([ "password", "confirm" ], "Password cannot be empty"); raiseError([ "password", "confirm" ], "Password cannot be empty");
} else { } else {
frozen = true; freeze();
fetch('https://localhost:4730/create_user', { fetch('https://localhost:4730/create_user', {
method: 'POST', method: 'POST',
mode: 'cors', mode: 'cors',
@ -65,8 +84,7 @@ window.addEventListener("load", (loadEvent) => {
.then(response =>{ .then(response =>{
const data = response.data; const data = response.data;
if (response.status === 200) { if (response.status === 200) {
window.sessionStorage.setItem('sessionKey', data.key); window.sessionStorage.setItem('session', JSON.stringify(data));
window.sessionStorage.setItem('id', data.id );
window.location.href = "/"; window.location.href = "/";
} else if (response.status === 500) { } else if (response.status === 500) {
raiseError([], "Internal server error :("); raiseError([], "Internal server error :(");
@ -83,7 +101,7 @@ window.addEventListener("load", (loadEvent) => {
.catch(error => { .catch(error => {
console.error(error); console.error(error);
raiseError([], "Server error :("); raiseError([], "Server error :(");
}).then(() => frozen = false); }).then(unfreeze);
} }
} }
}; };
@ -100,6 +118,8 @@ window.addEventListener("load", (loadEvent) => {
login.removeEventListener("click", switchToLoginAction); login.removeEventListener("click", switchToLoginAction);
login.addEventListener("click", loginAction); login.addEventListener("click", loginAction);
activeAction = loginAction;
} }
}; };
const loginAction = (clickEvent) => { const loginAction = (clickEvent) => {
@ -109,7 +129,7 @@ window.addEventListener("load", (loadEvent) => {
} else if (fields.password.value === '') { } else if (fields.password.value === '') {
raiseError([ "password" ], "Password cannot be empty"); raiseError([ "password" ], "Password cannot be empty");
} else { } else {
frozen = true; freeze();
fetch('https://localhost:4730/login', { fetch('https://localhost:4730/login', {
method: 'POST', method: 'POST',
mode: 'cors', mode: 'cors',
@ -123,8 +143,7 @@ window.addEventListener("load", (loadEvent) => {
const data = response.data; const data = response.data;
if (response.status === 200) { if (response.status === 200) {
console.log(data); console.log(data);
window.sessionStorage.setItem('sessionKey', data.key); window.sessionStorage.setItem('session', JSON.stringify(data));
window.sessionStorage.setItem('id', data.id );
window.location.href = "/"; window.location.href = "/";
} else if (response.status === 500) { } else if (response.status === 500) {
raiseError([], "Internal server error :("); raiseError([], "Internal server error :(");
@ -136,7 +155,7 @@ window.addEventListener("load", (loadEvent) => {
.catch(error => { .catch(error => {
console.error(error); console.error(error);
raiseError([], "Unknown error :("); raiseError([], "Unknown error :(");
}).then(() => frozen = false); }).then(unfreeze);
} }
} }
}; };
@ -144,34 +163,16 @@ window.addEventListener("load", (loadEvent) => {
guest.addEventListener("click", (clickEvent) => { guest.addEventListener("click", (clickEvent) => {
if (!frozen) { if (!frozen) {
frozen = true; window.location.href = '/';
fetch('https://localhost:4730/login?guest=true', {
method: 'POST',
mode: 'cors',
headers: {
'Content-Type': 'application/json'
},
body: "{}"
})
.then(async response => ({ status: response.status, data: await response.json() }))
.then(response => {
const data = response.data;
if (response.status === 200) {
console.log(data);
window.sessionStorage.setItem('sessionKey', data.key);
window.sessionStorage.setItem('id', data.id );
window.location.href = "/";
} else if (response.status === 500) {
raiseError([], "Internal server error :(");
} else {
raiseError([ "email", "password" ], "Email or password is incorrect");
fields.password.value = '';
}
})
.catch(error => {
console.error(error);
raiseError([], "Unknown error :(");
}).then(() => frozen = false);
} }
}); });
let activeAction = loginAction;
for (const field in fields) {
fields[field].addEventListener("keydown", (keyEvent) => {
if (keyEvent.key === "Enter") {
activeAction();
}
})
}
}); });

View file

@ -94,6 +94,21 @@ html, body {
background-color: var(--accent-value1); background-color: var(--accent-value1);
} }
.ap-button.disabled {
background-color: var(--accent-value1);
color: var(--accent-value0);
}
.ap-button.disabled:hover {
background-color: var(--accent-value1);
color: var(--accent-value0);
}
.ap-button.disabled:active {
background-color: var(--accent-value1);
color: var(--accent-value0);
}
#content-body { #content-body {
position: relative; position: relative;
@ -109,8 +124,6 @@ html, body {
} }
.page { .page {
z-index: 0;
box-sizing: border-box; box-sizing: border-box;
padding: 32px; padding: 32px;
@ -135,9 +148,6 @@ html, body {
border-radius: 8px; border-radius: 8px;
box-shadow: inset 0px 0px 8px 8px var(--shadow-color); box-shadow: inset 0px 0px 8px 8px var(--shadow-color);
position: relative;
z-index: -1;
} }
.page-subsection-wrapper { .page-subsection-wrapper {
@ -290,12 +300,12 @@ html, body {
} }
.list-page-filter-checkbox { .list-page-filter-checkbox {
width: 32px; width: 28px;
height: 32px; height: 28px;
background-color: var(--foreground); background-color: var(--foreground);
border: 3px solid var(--foreground-dark); border: 3px solid var(--foreground);
border-radius: 8px; border-radius: 8px;
transition-property: background-color, border-color; transition-property: background-color, border-color;

View file

@ -86,15 +86,27 @@ html, body {
box-shadow: 0px 0px 8px 8px var(--shadow-color); box-shadow: 0px 0px 8px 8px var(--shadow-color);
border-radius: 8px; border-radius: 8px;
}
#profile-info-pfp {
width: 100%;
height: 100%;
position: relative; position: relative;
z-index: -1;
} }
#profile-info-pfp-vignette { #profile-info-pfp-vignette {
top: 0%;
left: 0%;
width: 100%;
height: 100%;
box-shadow: inset 0px 0px 50px 30px var(--shadow-color); box-shadow: inset 0px 0px 50px 30px var(--shadow-color);
border-radius: 8px; border-radius: 8px;
position: absolute;
z-index: 1;
} }
#profile-info-pfp-img { #profile-info-pfp-img {
@ -103,7 +115,6 @@ html, body {
border-radius: 8px; border-radius: 8px;
position: relative; position: relative;
z-index: -1;
} }
.profile-list { .profile-list {

View file

@ -3,7 +3,7 @@
--element-spacing: 12px; --element-spacing: 12px;
--error: #FA7575; --error: #F95959;
} }
#login-page { #login-page {
@ -12,21 +12,35 @@
max-width: 1280px; max-width: 1280px;
} }
#login-flex {
display: flex;
flex-direction: row;
justify-content: center;
}
#login-subsection {
width: 100%;
}
#login-elements {
display: flex;
flex-direction: column;
align-items: center;
}
#login-header { #login-header {
box-sizing: border-box; box-sizing: border-box;
padding: 0 calc(25% - 64px); width: 50%;
width: 100%;
height: max-content; height: max-content;
} }
#login-form { #login-form {
box-sizing: border-box; box-sizing: border-box;
margin: 24px calc(25% - 64px) 0;
padding: 24px 0; padding: 24px 0;
width: 50%;
height: max-content; height: max-content;
background-color: var(--distinction); background-color: var(--distinction);
@ -99,6 +113,15 @@
} }
#warning { #warning {
color: var(--foreground); color: var(--error);
font-size: 24px; font-size: 24px;
text-align: center;
line-height: 40px;
}
#warning-message {
margin-top: 0;
color: var(--foreground);
font-size: 18px;
text-align: center;
} }

View file

@ -1,7 +1,7 @@
<div class="page-subsection"> <div class="page-subsection">
<div class="list-page-search page-subsection-chunk"> <div class="list-page-search page-subsection-chunk">
<label for="achievement-search">Search</label> <label for="achievement-search">Search</label>
<input id="achievement-search" type="text" placeholder="Name, Keyword, etc..." name="achievement-search"/> <input id="achievement-search" type="text" placeholder="Name" name="achievement-search"/>
</div> </div>
<div class="list-page-partitions"> <div class="list-page-partitions">
<div class="list-page-filter-partition"> <div class="list-page-filter-partition">
@ -14,7 +14,7 @@
<div class="page-subsection-wrapper"> <div class="page-subsection-wrapper">
<div id="from-games-owned-filter" class="list-page-filter"> <div id="from-games-owned-filter" class="list-page-filter">
<div class="list-page-filter-checkbox"></div> <div class="list-page-filter-checkbox"></div>
<p class="list-page-filter-name">From Games Owned</p> <p class="list-page-filter-name">My Games</p>
</div> </div>
<div id="in-progress-filter" class="list-page-filter"> <div id="in-progress-filter" class="list-page-filter">
<div class="list-page-filter-checkbox"></div> <div class="list-page-filter-checkbox"></div>

View file

@ -1,7 +1,7 @@
<div class="page-subsection"> <div class="page-subsection">
<div class="list-page-search page-subsection-chunk"> <div class="list-page-search page-subsection-chunk">
<label for="game-search">Search</label> <label for="game-search">Search</label>
<input id="game-search" type="text" placeholder="Name, Keyword, etc..." game-name="game-search" name="game-search"/> <input id="game-search" type="text" placeholder="Name" game-name="game-search" name="game-search"/>
</div> </div>
<div class="list-page-partitions"> <div class="list-page-partitions">
<div class="list-page-filter-partition"> <div class="list-page-filter-partition">

View file

@ -6,8 +6,9 @@
<div class="page-subheader-separator"></div> <div class="page-subheader-separator"></div>
</div> </div>
<div id="profile-info-pfp-border" class="page-subsection-chunk"> <div id="profile-info-pfp-border" class="page-subsection-chunk">
<div id="profile-info-pfp-vignette"> <div id="profile-info-pfp">
<img id="profile-info-pfp-img" src="res/guest_pfp.png" alt="User's Profile Pictuer" /> <img id="profile-info-pfp-img" src="res/guest_pfp.png" alt="User's Profile Pictuer" />
<div id="profile-info-pfp-vignette"></div>
</div> </div>
</div> </div>
</div> </div>
@ -41,7 +42,7 @@
<div id="profile-section-2"> <div id="profile-section-2">
<div id="profile-games" class="page-subsection"> <div id="profile-games" class="page-subsection">
<div class="page-subsection-wrapper"> <div class="page-subsection-wrapper">
<div class="page-subheader"> <div id="profile-games-header" class="page-subheader">
<p class="page-subheader-text link">Games</p> <p class="page-subheader-text link">Games</p>
<div class="page-subheader-separator"></div> <div class="page-subheader-separator"></div>
</div> </div>
@ -95,7 +96,7 @@
</div> </div>
<div id="profile-achievements" class="page-subsection"> <div id="profile-achievements" class="page-subsection">
<div class="page-subsection-wrapper"> <div class="page-subsection-wrapper">
<div class="page-subheader"> <div id="profile-achievements-header" class="page-subheader">
<p class="page-subheader-text link">Achievements</p> <p class="page-subheader-text link">Achievements</p>
<div class="page-subheader-separator"></div> <div class="page-subheader-separator"></div>
</div> </div>

View file

@ -33,7 +33,11 @@ CREATE TABLE [User] (
Username VARCHAR(32) NOT NULL, Username VARCHAR(32) NOT NULL,
[Password] CHAR(64) NOT NULL, [Password] CHAR(64) NOT NULL,
[Salt] CHAR(32) NOT NULL, [Salt] CHAR(32) NOT NULL,
Verified BIT NOT NULL CONSTRAINT VerifiedDefault DEFAULT 0 Hue INT NOT NULL
CONSTRAINT HueDefault DEFAULT 0
CONSTRAINT HueConstraint CHECK (0 <= Hue AND Hue <= 360),
Verified BIT NOT NULL
CONSTRAINT VerifiedDefault DEFAULT 0
PRIMARY KEY(ID) PRIMARY KEY(ID)
) )

View file

@ -1,4 +1,6 @@
CREATE PROCEDURE GetUserLogin( CREATE PROCEDURE GetUserLogin(
@email VARCHAR(254) @email VARCHAR(254)
) AS ) AS
SELECT Id, Salt, [Password] FROM [User] WHERE Email = @email BEGIN TRANSACTION
SELECT Id, Salt, [Password], Hue FROM [User] WHERE Email = @email
COMMIT TRANSACTION