Revamped UI...ish
This commit is contained in:
parent
13736d0ef9
commit
40a0e4046a
26 changed files with 2053 additions and 621 deletions
|
@ -1,7 +1,6 @@
|
||||||
package achievements;
|
package achievements;
|
||||||
|
|
||||||
import achievements.misc.Password;
|
import achievements.misc.DbConnectionService;
|
||||||
import achievements.services.DbConnectionService;
|
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package achievements.controllers;
|
package achievements.controllers;
|
||||||
|
|
||||||
import achievements.data.Achievements;
|
import achievements.data.Achievements;
|
||||||
import achievements.data.User;
|
|
||||||
import achievements.data.Games;
|
import achievements.data.Games;
|
||||||
import achievements.data.InternalError;
|
import achievements.data.InternalError;
|
||||||
import achievements.services.DbService;
|
import achievements.services.DbService;
|
||||||
|
@ -13,7 +12,6 @@ import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import static org.springframework.web.bind.annotation.RequestMethod.GET;
|
import static org.springframework.web.bind.annotation.RequestMethod.GET;
|
||||||
import static org.springframework.web.bind.annotation.RequestMethod.POST;
|
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
public class Controller {
|
public class Controller {
|
||||||
|
@ -64,47 +62,4 @@ public class Controller {
|
||||||
return new ResponseEntity("{}", HttpStatus.INTERNAL_SERVER_ERROR);
|
return new ResponseEntity("{}", HttpStatus.INTERNAL_SERVER_ERROR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Acceptable codes
|
|
||||||
* 0 => Success
|
|
||||||
* 1 => Email already registered
|
|
||||||
*
|
|
||||||
* -1 => Unknown error
|
|
||||||
*/
|
|
||||||
@RequestMapping(value = "/create_user", method = POST, consumes = "application/json", produces = "application/json")
|
|
||||||
public ResponseEntity createUser(@RequestBody User user) {
|
|
||||||
var status = db.createUser(user);
|
|
||||||
if (status == 0) {
|
|
||||||
return ResponseEntity.ok("{ \"key\": \"aoeuhtns\" }");
|
|
||||||
//var sessionKey = db.generateSessionKey(user);
|
|
||||||
} else {
|
|
||||||
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("{ \"code\": " + status + " }");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DO NOT RETURN CODE DIRECTLY!
|
|
||||||
*
|
|
||||||
* User should only ever recieve -1, 0, or 1. The specific authentication error should be hidden.
|
|
||||||
*
|
|
||||||
* Acceptable codes
|
|
||||||
* 0 => Success
|
|
||||||
* 1 => Unregistered email address
|
|
||||||
* 2 => Incorrect password
|
|
||||||
*
|
|
||||||
* -1 => Unknown error
|
|
||||||
*/
|
|
||||||
@RequestMapping(value = "/login", method = POST, consumes = "application/json", produces = "application/json")
|
|
||||||
public ResponseEntity login(@RequestBody User user) {
|
|
||||||
var status = db.login(user);
|
|
||||||
if (status == 0) {
|
|
||||||
return ResponseEntity.ok("{ \"key\": \"aoeuhtns\" }");
|
|
||||||
} else if (status > 0) {
|
|
||||||
// Hardcoded 1 response code
|
|
||||||
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("{ \"code\": 1 }");
|
|
||||||
} else {
|
|
||||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("{ \"code\": " + status + " }");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
package achievements.controllers;
|
||||||
|
|
||||||
|
import achievements.data.User;
|
||||||
|
import achievements.services.AuthenticationService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import static org.springframework.web.bind.annotation.RequestMethod.POST;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
public class LoginController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AuthenticationService authService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Acceptable codes
|
||||||
|
* 0 => Success
|
||||||
|
* 1 => Email already registered
|
||||||
|
*
|
||||||
|
* -1 => Unknown error
|
||||||
|
*/
|
||||||
|
@RequestMapping(value = "/create_user", method = POST, consumes = "application/json", produces = "application/json")
|
||||||
|
public ResponseEntity createUser(@RequestBody User user) {
|
||||||
|
var response = authService.createUser(user);
|
||||||
|
if (response.status == 0) {
|
||||||
|
return ResponseEntity.ok("{ \"key\": \"" + authService.session().generate(response.id) + "\", \"id\": " + response.id + " }");
|
||||||
|
} else if (response.status > 0) {
|
||||||
|
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("{ \"code\": " + response.status + " }");
|
||||||
|
} else {
|
||||||
|
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("{ \"code\": " + response.status + " }");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DO NOT RETURN CODE DIRECTLY!
|
||||||
|
*
|
||||||
|
* User should only ever recieve -1, 0, or 1. The specific authentication error should be hidden.
|
||||||
|
*
|
||||||
|
* Acceptable codes
|
||||||
|
* 0 => Success
|
||||||
|
* 1 => Unregistered email address
|
||||||
|
* 2 => Incorrect password
|
||||||
|
*
|
||||||
|
* -1 => Unknown error
|
||||||
|
*/
|
||||||
|
@RequestMapping(value = "/login", method = POST, consumes = "application/json", produces = "application/json")
|
||||||
|
public ResponseEntity login(@RequestParam(value = "guest", required = false) boolean guest, @RequestBody User user) {
|
||||||
|
var response = guest ?
|
||||||
|
authService.GUEST :
|
||||||
|
authService.login(user);
|
||||||
|
if (response.status == 0) {
|
||||||
|
return ResponseEntity.ok("{ \"key\": \"" + authService.session().generate(response.id) + "\", \"id\": " + response.id + " }");
|
||||||
|
} else if (response.status > 0) {
|
||||||
|
// Hardcoded 1 response code
|
||||||
|
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("{ \"code\": 1 }");
|
||||||
|
} else {
|
||||||
|
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("{ \"code\": " + response.status + " }");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package achievements.services;
|
package achievements.misc;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
70
backend/src/main/java/achievements/misc/HashManager.java
Normal file
70
backend/src/main/java/achievements/misc/HashManager.java
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
package achievements.misc;
|
||||||
|
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
public class HashManager {
|
||||||
|
|
||||||
|
private static final Random RANDOM = new SecureRandom();
|
||||||
|
|
||||||
|
public static byte[] hash(byte[] salt, byte[] password) {
|
||||||
|
try {
|
||||||
|
var concat = new byte[salt.length + password.length];
|
||||||
|
int i = 0;
|
||||||
|
for (; i < salt.length; ++i) {
|
||||||
|
concat[i] = salt[i];
|
||||||
|
}
|
||||||
|
for (int j = 0; j < password.length; ++j) {
|
||||||
|
concat[i + j] = password[j];
|
||||||
|
}
|
||||||
|
|
||||||
|
var md = MessageDigest.getInstance("SHA-256");
|
||||||
|
return md.digest(concat);
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String encode(byte[] bytes) {
|
||||||
|
var chars = new char[bytes.length << 1];
|
||||||
|
for (int i = 0; i < bytes.length; ++i) {
|
||||||
|
chars[(i << 1) ] = toHex(bytes[i] >> 0);
|
||||||
|
chars[(i << 1) + 1] = toHex(bytes[i] >> 4);
|
||||||
|
}
|
||||||
|
return new String(chars);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] decode(String data) {
|
||||||
|
var decoded = new byte[data.length() >> 1];
|
||||||
|
for (int i = 0; i < data.length(); i += 2) {
|
||||||
|
int currentByte =
|
||||||
|
(fromHex(data.charAt(i )) ) |
|
||||||
|
(fromHex(data.charAt(i + 1)) << 4);
|
||||||
|
decoded[i >> 1] = (byte) (currentByte & 0xFF);
|
||||||
|
}
|
||||||
|
return decoded;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] generateBytes(int length) {
|
||||||
|
var bytes = new byte[length];
|
||||||
|
RANDOM.nextBytes(bytes);
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static char toHex(int halfByte) {
|
||||||
|
halfByte = halfByte & 0xF;
|
||||||
|
if (0 <= halfByte && halfByte <= 9 ) return (char) (halfByte + '0' );
|
||||||
|
if (10 <= halfByte && halfByte <= 15) return (char) (halfByte + 'a' - 10);
|
||||||
|
return '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int fromHex(char c) {
|
||||||
|
if ('0' <= c && c <= '9') return c - '0';
|
||||||
|
if ('A' <= c && c <= 'F') return c - 'A' + 10;
|
||||||
|
if ('a' <= c && c <= 'f') return c - 'a' + 10;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,14 +1,7 @@
|
||||||
package achievements.misc;
|
package achievements.misc;
|
||||||
|
|
||||||
import java.security.MessageDigest;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.security.SecureRandom;
|
|
||||||
import java.util.Random;
|
|
||||||
|
|
||||||
public class Password {
|
public class Password {
|
||||||
|
|
||||||
private static final Random RANDOM = new SecureRandom();
|
|
||||||
|
|
||||||
public final String salt;
|
public final String salt;
|
||||||
public final String hash;
|
public final String hash;
|
||||||
|
|
||||||
|
@ -19,19 +12,17 @@ public class Password {
|
||||||
|
|
||||||
public static Password generate(String password) {
|
public static Password generate(String password) {
|
||||||
// Generate the salt
|
// Generate the salt
|
||||||
var salt = new byte[16]; // 128 bits
|
var salt = HashManager.generateBytes(16); // 128 bits
|
||||||
RANDOM.nextBytes(salt);
|
|
||||||
|
|
||||||
return new Password(
|
return new Password(
|
||||||
encode(salt),
|
HashManager.encode(salt),
|
||||||
encode(hash(salt, password.getBytes()))
|
HashManager.encode(HashManager.hash(salt, password.getBytes()))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean validate(String salt, String password, String hash) {
|
public static boolean validate(String salt, String password, String hash) {
|
||||||
System.out.println(salt + ", " + password);
|
var srcHash = HashManager.hash(HashManager.decode(salt), password.getBytes());
|
||||||
var srcHash = hash(decode(salt), password.getBytes());
|
var targetHash = HashManager.decode(hash);
|
||||||
var targetHash = decode(hash);
|
|
||||||
for (int i = 0; i < srcHash.length; ++i) {
|
for (int i = 0; i < srcHash.length; ++i) {
|
||||||
if (srcHash[i] != targetHash[i]) {
|
if (srcHash[i] != targetHash[i]) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -39,57 +30,4 @@ public class Password {
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] hash(byte[] salt, byte[] password) {
|
|
||||||
try {
|
|
||||||
var concat = new byte[salt.length + password.length];
|
|
||||||
int i = 0;
|
|
||||||
for (; i < salt.length; ++i) {
|
|
||||||
concat[i] = salt[i];
|
|
||||||
}
|
|
||||||
for (int j = 0; j < password.length; ++j) {
|
|
||||||
concat[i + j] = password[j];
|
|
||||||
}
|
|
||||||
|
|
||||||
var md = MessageDigest.getInstance("SHA-256");
|
|
||||||
return md.digest(concat);
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String encode(byte[] bytes) {
|
|
||||||
var chars = new char[bytes.length << 1];
|
|
||||||
for (int i = 0; i < bytes.length; ++i) {
|
|
||||||
chars[(i << 1) ] = toHex(bytes[i] >> 0);
|
|
||||||
chars[(i << 1) + 1] = toHex(bytes[i] >> 4);
|
|
||||||
}
|
|
||||||
return new String(chars);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static byte[] decode(String data) {
|
|
||||||
var decoded = new byte[data.length() >> 1];
|
|
||||||
for (int i = 0; i < data.length(); i += 2) {
|
|
||||||
int currentByte =
|
|
||||||
(fromHex(data.charAt(i )) ) |
|
|
||||||
(fromHex(data.charAt(i + 1)) << 4);
|
|
||||||
decoded[i >> 1] = (byte) (currentByte & 0xFF);
|
|
||||||
}
|
|
||||||
return decoded;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static char toHex(int halfByte) {
|
|
||||||
halfByte = halfByte & 0xF;
|
|
||||||
if (0 <= halfByte && halfByte <= 9 ) return (char) (halfByte + '0' );
|
|
||||||
if (10 <= halfByte && halfByte <= 15) return (char) (halfByte + 'a' - 10);
|
|
||||||
return '0';
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int fromHex(char c) {
|
|
||||||
if ('0' <= c && c <= '9') return c - '0';
|
|
||||||
if ('A' <= c && c <= 'F') return c - 'A' + 10;
|
|
||||||
if ('a' <= c && c <= 'f') return c - 'a' + 10;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
28
backend/src/main/java/achievements/misc/SessionManager.java
Normal file
28
backend/src/main/java/achievements/misc/SessionManager.java
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
package achievements.misc;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
public class SessionManager {
|
||||||
|
|
||||||
|
private HashMap<String, Integer> session;
|
||||||
|
|
||||||
|
public SessionManager() {
|
||||||
|
session = new HashMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String generate(Integer user) {
|
||||||
|
var key = HashManager.encode(HashManager.generateBytes(16));
|
||||||
|
session.put(key, user);
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String guest() {
|
||||||
|
var key = HashManager.encode(HashManager.generateBytes(16));
|
||||||
|
session.put(key, null);
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getUser(String key) {
|
||||||
|
return session.get(key);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,106 @@
|
||||||
|
package achievements.services;
|
||||||
|
|
||||||
|
import achievements.data.User;
|
||||||
|
import achievements.misc.DbConnectionService;
|
||||||
|
import achievements.misc.Password;
|
||||||
|
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 AuthenticationService {
|
||||||
|
|
||||||
|
public static class LoginResponse {
|
||||||
|
public int status;
|
||||||
|
public Integer id;
|
||||||
|
|
||||||
|
public LoginResponse() {
|
||||||
|
this.status = 0;
|
||||||
|
this.id = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LoginResponse(int status) {
|
||||||
|
this.status = status;
|
||||||
|
this.id = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LoginResponse(int status, int id) {
|
||||||
|
this.status = status;
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final LoginResponse GUEST = new LoginResponse();
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private DbConnectionService dbs;
|
||||||
|
private Connection db;
|
||||||
|
|
||||||
|
private SessionManager session;
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
private void init() {
|
||||||
|
db = dbs.getConnection();
|
||||||
|
session = new SessionManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
public LoginResponse createUser(User user) {
|
||||||
|
if (!user.getEmail().matches(".+@\\w+\\.\\w+")) {
|
||||||
|
return new LoginResponse(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
var statement = db.prepareCall("{? = call CreateUser(?, ?, ?, ?, ?)}");
|
||||||
|
statement.registerOutParameter(1, Types.INTEGER);
|
||||||
|
statement.setString(2, user.getEmail());
|
||||||
|
statement.setString(3, user.getUsername());
|
||||||
|
|
||||||
|
var password = Password.generate(user.getPassword());
|
||||||
|
statement.setString(4, password.salt);
|
||||||
|
statement.setString(5, password.hash);
|
||||||
|
|
||||||
|
statement.registerOutParameter(6, Types.INTEGER);
|
||||||
|
|
||||||
|
statement.execute();
|
||||||
|
var response = new LoginResponse(statement.getInt(1), statement.getInt(6));
|
||||||
|
statement.close();
|
||||||
|
|
||||||
|
return response;
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return new LoginResponse(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LoginResponse login(User user) {
|
||||||
|
var response = new LoginResponse(-1);
|
||||||
|
try {
|
||||||
|
var statement = db.prepareCall("{call GetUserLogin(?)}");
|
||||||
|
statement.setString(1, user.email);
|
||||||
|
|
||||||
|
var result = statement.executeQuery();
|
||||||
|
if (result.next()) {
|
||||||
|
var salt = result.getString("Salt");
|
||||||
|
var hash = result.getString("Password");
|
||||||
|
if (Password.validate(salt, user.getPassword(), hash)) {
|
||||||
|
response = new LoginResponse(0, result.getInt("ID"));
|
||||||
|
} else {
|
||||||
|
response = new LoginResponse(2);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
response = new LoginResponse(1);
|
||||||
|
}
|
||||||
|
statement.close();
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SessionManager session() {
|
||||||
|
return session;
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,8 +2,8 @@ package achievements.services;
|
||||||
|
|
||||||
import achievements.data.Achievements;
|
import achievements.data.Achievements;
|
||||||
import achievements.data.Games;
|
import achievements.data.Games;
|
||||||
import achievements.data.User;
|
import achievements.misc.DbConnectionService;
|
||||||
import achievements.misc.Password;
|
import achievements.misc.SessionManager;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@ -18,7 +18,9 @@ public class DbService {
|
||||||
private Connection db;
|
private Connection db;
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
private void init() { db = dbs.getConnection(); }
|
private void init() {
|
||||||
|
db = dbs.getConnection();
|
||||||
|
}
|
||||||
|
|
||||||
public Achievements getAchievements(String gameName) {
|
public Achievements getAchievements(String gameName) {
|
||||||
try {
|
try {
|
||||||
|
@ -87,48 +89,4 @@ public class DbService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int createUser(User user) {
|
|
||||||
try {
|
|
||||||
var statement = db.prepareCall("{? = call CreateUser(?, ?, ?, ?)}");
|
|
||||||
statement.registerOutParameter(1, Types.INTEGER);
|
|
||||||
statement.setString(2, user.getEmail());
|
|
||||||
statement.setString(3, user.getUsername());
|
|
||||||
|
|
||||||
var password = Password.generate(user.getPassword());
|
|
||||||
statement.setString(4, password.salt);
|
|
||||||
statement.setString(5, password.hash);
|
|
||||||
|
|
||||||
statement.execute();
|
|
||||||
var code = statement.getInt(1);
|
|
||||||
statement.close();
|
|
||||||
|
|
||||||
return code;
|
|
||||||
} catch (SQLException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int login(User user) {
|
|
||||||
try {
|
|
||||||
var statement = db.prepareStatement("SELECT Salt, Password FROM [dbo].[User] WHERE Email = ?");
|
|
||||||
statement.setString(1, user.email);
|
|
||||||
|
|
||||||
var result = statement.executeQuery();
|
|
||||||
if (result.next()) {
|
|
||||||
var salt = result.getString("Salt");
|
|
||||||
var hash = result.getString("Password");
|
|
||||||
if (Password.validate(salt, user.getPassword(), hash)) {
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
} catch (SQLException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,10 +23,12 @@
|
||||||
<div id="content-body">
|
<div id="content-body">
|
||||||
<template data-template="content-body: List<Basic>">
|
<template data-template="content-body: List<Basic>">
|
||||||
<div id="${page}-page" class="page">
|
<div id="${page}-page" class="page">
|
||||||
|
<div class="page-subsection">
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<p class="page-header-text">${title}</p>
|
<p class="page-header-text">${title}</p>
|
||||||
<div class="page-header-separator"></div>
|
<div class="page-header-separator"></div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<template data-template="extern-${page}-page: Extern"></template>
|
<template data-template="extern-${page}-page: Extern"></template>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -21,16 +21,17 @@
|
||||||
<div class="page-subheader-separator"></div>
|
<div class="page-subheader-separator"></div>
|
||||||
</div>
|
</div>
|
||||||
<div id="login-form">
|
<div id="login-form">
|
||||||
<p id="error-message">Egg</p>
|
<p id="error-message" class="form-row top multiline">Egg</p>
|
||||||
<input id="email" class="login-field" type="text" placeholder="Email"></input>
|
<input id="email" class="login-field form-row" type="text" placeholder="Email"></input>
|
||||||
<input id="username" class="login-field" type="text" placeholder="Username"></input>
|
<input id="username" class="login-field form-row" type="text" placeholder="Username"></input>
|
||||||
<input id="password" class="login-field" type="password" placeholder="Password"></input>
|
<input id="password" class="login-field form-row" type="password" placeholder="Password"></input>
|
||||||
<input id="confirm" class="login-field" type="password" placeholder="Confirm your password"></input>
|
<input id="confirm" class="login-field form-row" type="password" placeholder="Confirm your password"></input>
|
||||||
<div id="login-buttons">
|
<div id="button-row" class="form-row">
|
||||||
<div id="create-user-button" class="ap-button login">Create Account ></div>
|
<div id="create-user-button" class="ap-button login">Create Account</div>
|
||||||
<div id="login-button" class="ap-button login">Login</div>
|
<div id="guest-login-button" class="ap-button login">Continue as Guest</div>
|
||||||
</div>
|
</div>
|
||||||
<p id="warning">WARNING! The security of this project is questionable at best. Please refrain from using any truly sensitive data.</p>
|
<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>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
BIN
frontend/webpage/res/guest_pfp.png
Normal file
BIN
frontend/webpage/res/guest_pfp.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 92 KiB |
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 41 KiB |
|
@ -8,6 +8,7 @@ window.addEventListener("load", (loadEvent) => {
|
||||||
|
|
||||||
const createUser = document.querySelector("#create-user-button");
|
const createUser = document.querySelector("#create-user-button");
|
||||||
const login = document.querySelector("#login-button");
|
const login = document.querySelector("#login-button");
|
||||||
|
const guest = document.querySelector("#guest-login-button");
|
||||||
|
|
||||||
const header = document.querySelector("#login-header-text");
|
const header = document.querySelector("#login-header-text");
|
||||||
const error = document.querySelector("#error-message");
|
const error = document.querySelector("#error-message");
|
||||||
|
@ -31,11 +32,7 @@ window.addEventListener("load", (loadEvent) => {
|
||||||
if (!frozen) {
|
if (!frozen) {
|
||||||
fields.username.style.display = "block";
|
fields.username.style.display = "block";
|
||||||
fields.confirm.style.display = "block";
|
fields.confirm.style.display = "block";
|
||||||
header.textContent = "Create User";
|
header.textContent = "Create Account";
|
||||||
login.style.flexGrow = "0";
|
|
||||||
login.textContent = "< Login";
|
|
||||||
createUser.style.flexGrow = "1";
|
|
||||||
createUser.textContent = "Create User";
|
|
||||||
|
|
||||||
createUser.removeEventListener("click", switchToCreateAction);
|
createUser.removeEventListener("click", switchToCreateAction);
|
||||||
createUser.addEventListener("click", createUserAction);
|
createUser.addEventListener("click", createUserAction);
|
||||||
|
@ -65,10 +62,11 @@ window.addEventListener("load", (loadEvent) => {
|
||||||
body: JSON.stringify({ email: fields.email.value, username: fields.username.value, password: fields.password.value })
|
body: JSON.stringify({ email: fields.email.value, username: fields.username.value, password: fields.password.value })
|
||||||
})
|
})
|
||||||
.then(async response => ({ status: response.status, data: await response.json() }))
|
.then(async response => ({ status: response.status, data: await response.json() }))
|
||||||
.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('sessionKey', data.key);
|
||||||
|
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 :(");
|
||||||
|
@ -76,6 +74,9 @@ window.addEventListener("load", (loadEvent) => {
|
||||||
if (data.code === 1) {
|
if (data.code === 1) {
|
||||||
raiseError([ "email" ], "A user with that email is already registered");
|
raiseError([ "email" ], "A user with that email is already registered");
|
||||||
fields.email.value = '';
|
fields.email.value = '';
|
||||||
|
} else if (data.code === 2) {
|
||||||
|
raiseError([ "email" ], "Invalid email address");
|
||||||
|
fields.email.value = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -93,10 +94,6 @@ window.addEventListener("load", (loadEvent) => {
|
||||||
fields.username.style.display = "none";
|
fields.username.style.display = "none";
|
||||||
fields.confirm.style.display = "none";
|
fields.confirm.style.display = "none";
|
||||||
header.textContent = "Login";
|
header.textContent = "Login";
|
||||||
login.style.flexGrow = "1"
|
|
||||||
login.textContent = "Login";
|
|
||||||
createUser.style.flexGrow = "0";
|
|
||||||
createUser.textContent = "Create User >";
|
|
||||||
|
|
||||||
createUser.removeEventListener("click", createUserAction);
|
createUser.removeEventListener("click", createUserAction);
|
||||||
createUser.addEventListener("click", switchToCreateAction);
|
createUser.addEventListener("click", switchToCreateAction);
|
||||||
|
@ -125,7 +122,9 @@ window.addEventListener("load", (loadEvent) => {
|
||||||
.then(response => {
|
.then(response => {
|
||||||
const data = response.data;
|
const data = response.data;
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
|
console.log(data);
|
||||||
window.sessionStorage.setItem('sessionKey', data.key);
|
window.sessionStorage.setItem('sessionKey', data.key);
|
||||||
|
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 :(");
|
||||||
|
@ -142,4 +141,37 @@ window.addEventListener("load", (loadEvent) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
login.addEventListener("click", loginAction);
|
login.addEventListener("click", loginAction);
|
||||||
|
|
||||||
|
guest.addEventListener("click", (clickEvent) => {
|
||||||
|
if (!frozen) {
|
||||||
|
frozen = true;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
|
@ -1,3 +1,7 @@
|
||||||
|
:root {
|
||||||
|
--shadow-color: rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
html, body {
|
html, body {
|
||||||
background-color: var(--background-dark);
|
background-color: var(--background-dark);
|
||||||
|
|
||||||
|
@ -27,7 +31,7 @@ html, body {
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
box-shadow: 0px 0px 5px 10px rgba(0, 0, 0, 0.5);
|
box-shadow: 0px 0px 5px 10px var(--shadow-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar-section {
|
.navbar-section {
|
||||||
|
@ -67,20 +71,17 @@ html, body {
|
||||||
}
|
}
|
||||||
|
|
||||||
.ap-button {
|
.ap-button {
|
||||||
box-sizing: border-box;
|
|
||||||
|
|
||||||
padding: 12px 16px;
|
|
||||||
|
|
||||||
color: var(--foreground);
|
color: var(--foreground);
|
||||||
background-color: var(--accent-value2);
|
background-color: var(--accent-value2);
|
||||||
|
|
||||||
font-size: 18px;
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
|
||||||
cursor: default;
|
cursor: default;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
transition-property: background-color;
|
transition-property: background-color;
|
||||||
transition-duration: 0.15s;
|
transition-duration: 0.15s;
|
||||||
}
|
}
|
||||||
|
@ -112,33 +113,61 @@ html, body {
|
||||||
|
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|
||||||
padding: 0px 64px;
|
padding: 32px;
|
||||||
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: max-content;
|
height: max-content;
|
||||||
min-height: 100%;
|
min-height: 100%;
|
||||||
|
|
||||||
background-color: var(--background);
|
background-color: var(--background);
|
||||||
box-shadow: 0px 0px 5px 10px rgba(0, 0, 0, 0.5);
|
box-shadow: 0px 0px 5px 10px var(--shadow-color);
|
||||||
|
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.page-subsection {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 32px;
|
||||||
|
padding: 16px;
|
||||||
|
|
||||||
|
background-color: var(--background-dark);
|
||||||
|
|
||||||
|
border-radius: 8px;
|
||||||
|
|
||||||
|
box-shadow: inset 0px 0px 8px 8px var(--shadow-color);
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-subsection-wrapper {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-subsection-chunk {
|
||||||
|
box-shadow: 0px 0px 8px 8px var(--shadow-color);
|
||||||
|
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
.page-header {
|
.page-header {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|
||||||
padding: 64px 0px;
|
|
||||||
|
|
||||||
width: 100%;
|
|
||||||
height: max-content;
|
height: max-content;
|
||||||
|
|
||||||
|
margin: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-subheader {
|
||||||
|
margin-bottom: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-header-text,
|
.page-header-text,
|
||||||
.page-subheader-text {
|
.page-subheader-text {
|
||||||
width: max-content;
|
width: max-content;
|
||||||
|
|
||||||
margin: 0;
|
margin: 0 0 0.25em;
|
||||||
margin-bottom: 0.25em;
|
|
||||||
|
|
||||||
color: var(--foreground);
|
color: var(--foreground);
|
||||||
|
|
||||||
|
@ -157,11 +186,11 @@ html, body {
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-header-text {
|
.page-header-text {
|
||||||
font-size: 64px;
|
font-size: 48px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-subheader-text {
|
.page-subheader-text {
|
||||||
font-size: 48px;
|
font-size: 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-header-separator,
|
.page-header-separator,
|
||||||
|
@ -175,9 +204,7 @@ html, body {
|
||||||
.list-page-search {
|
.list-page-search {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|
||||||
padding: 0px 64px 32px;
|
margin: 16px;
|
||||||
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
@ -197,8 +224,6 @@ html, body {
|
||||||
|
|
||||||
.list-page-search > label {
|
.list-page-search > label {
|
||||||
background-color: var(--accent-value2);
|
background-color: var(--accent-value2);
|
||||||
|
|
||||||
border-radius: 8px 0px 0px 8px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-page-search > label:hover {
|
.list-page-search > label:hover {
|
||||||
|
@ -216,7 +241,6 @@ html, body {
|
||||||
background-color: var(--distinction);
|
background-color: var(--distinction);
|
||||||
|
|
||||||
border: 0;
|
border: 0;
|
||||||
border-radius: 0px 8px 8px 0px;
|
|
||||||
|
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
|
|
||||||
|
@ -235,8 +259,6 @@ html, body {
|
||||||
.list-page-partitions {
|
.list-page-partitions {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|
||||||
padding: 32px 64px;
|
|
||||||
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: max-content;
|
height: max-content;
|
||||||
|
|
||||||
|
@ -251,9 +273,11 @@ html, body {
|
||||||
max-width: 640px;
|
max-width: 640px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-page-filter {
|
.list-page-filter-chunk {
|
||||||
margin-top: 16px;
|
background-color: var(--distinction);
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-page-filter {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -269,9 +293,9 @@ html, body {
|
||||||
width: 32px;
|
width: 32px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
|
|
||||||
background-color: var(--background);
|
background-color: var(--foreground);
|
||||||
|
|
||||||
border: 3px solid var(--distinction);
|
border: 3px solid var(--foreground-dark);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
|
|
||||||
transition-property: background-color, border-color;
|
transition-property: background-color, border-color;
|
||||||
|
@ -279,7 +303,7 @@ html, body {
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-page-filter:hover > .list-page-filter-checkbox {
|
.list-page-filter:hover > .list-page-filter-checkbox {
|
||||||
background-color: var(--background);
|
background-color: var(--foreground);
|
||||||
border-color: var(--selected-accent1);
|
border-color: var(--selected-accent1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -307,8 +331,6 @@ html, body {
|
||||||
.list-page-list-partition {
|
.list-page-list-partition {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|
||||||
padding-left: 64px;
|
|
||||||
|
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,7 @@ html, body {
|
||||||
}
|
}
|
||||||
|
|
||||||
#profile-page {
|
#profile-page {
|
||||||
max-width: 1920px;
|
max-width: 1600px;
|
||||||
|
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
@ -67,43 +67,54 @@ html, body {
|
||||||
}
|
}
|
||||||
|
|
||||||
#profile-info {
|
#profile-info {
|
||||||
box-sizing: border-box;
|
width: 50%;
|
||||||
|
|
||||||
width: max-content;
|
|
||||||
height: max-content;
|
height: max-content;
|
||||||
|
|
||||||
max-width: 50%;
|
max-width: 480px;
|
||||||
|
|
||||||
padding: 0px 64px;
|
|
||||||
padding-bottom: 128px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#profile-info-pfp {
|
#profile-info-pfp-border {
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
padding: 24px;
|
||||||
|
background-color: var(--distinction);
|
||||||
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 640px;
|
max-width: 640px;
|
||||||
|
height: max-content;
|
||||||
|
|
||||||
margin-bottom: 1.25em;
|
box-shadow: 0px 0px 8px 8px var(--shadow-color);
|
||||||
|
|
||||||
|
border-radius: 8px;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
z-index: -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#profile-info-name {
|
#profile-info-pfp-vignette {
|
||||||
margin: 0;
|
box-shadow: inset 0px 0px 50px 30px var(--shadow-color);
|
||||||
|
|
||||||
padding: 16px 0px;
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
font-size: 42px;
|
#profile-info-pfp-img {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
border-radius: 8px;
|
||||||
|
|
||||||
color: var(--foreground);
|
position: relative;
|
||||||
|
z-index: -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.profile-list {
|
.profile-list {
|
||||||
margin-top: 1.25em;
|
|
||||||
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: max-content;
|
height: max-content;
|
||||||
|
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
|
box-shadow: 0px 0px 8px 8px var(--shadow-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.profile-entry {
|
.profile-entry {
|
||||||
|
@ -117,13 +128,9 @@ html, body {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
background-color: var(--distinction);
|
background-color: var(--distinction);
|
||||||
|
|
||||||
border-bottom: 1px solid var(--background);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.profile-entry.accented {
|
.profile-entry.accented {
|
||||||
border-bottom: 1px solid var(--accent-value0);
|
|
||||||
|
|
||||||
background-color: var(--accent-value1);
|
background-color: var(--accent-value1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,15 +154,40 @@ html, body {
|
||||||
|
|
||||||
.profile-entry-icon {
|
.profile-entry-icon {
|
||||||
width: 64px;
|
width: 64px;
|
||||||
|
|
||||||
|
flex-grow: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.profile-entry-text {
|
.profile-entry-text {
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0px 16px;
|
padding: 0px 16px;
|
||||||
|
|
||||||
|
height: 100%;
|
||||||
|
line-height: 64px;
|
||||||
|
|
||||||
color: var(--foreground);
|
color: var(--foreground);
|
||||||
|
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
|
|
||||||
|
border-top: 1px solid var(--background);
|
||||||
|
|
||||||
|
flex-grow: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top > .profile-entry-text {
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.accented > .profile-entry-text {
|
||||||
|
border-color: var(--accent-value0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-entry-text.platform-name,
|
||||||
|
.profile-entry-text.game-name,
|
||||||
|
.profile-entry-text.achievement-name {
|
||||||
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.profile-entry-text.accented {
|
.profile-entry-text.accented {
|
||||||
|
@ -167,12 +199,6 @@ html, body {
|
||||||
}
|
}
|
||||||
|
|
||||||
#profile-platforms {
|
#profile-platforms {
|
||||||
box-sizing: border-box;
|
|
||||||
|
|
||||||
height: max-content;
|
|
||||||
|
|
||||||
padding: 0px 64px;
|
|
||||||
|
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,18 +214,8 @@ html, body {
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
#profile-games {
|
#profile-games,
|
||||||
box-sizing: border-box;
|
|
||||||
padding: 0px 64px 64px;
|
|
||||||
|
|
||||||
width: 50%;
|
|
||||||
height: max-content;
|
|
||||||
}
|
|
||||||
|
|
||||||
#profile-achievements {
|
#profile-achievements {
|
||||||
box-sizing: border-box;
|
|
||||||
padding: 0px 64px 64px;
|
|
||||||
|
|
||||||
width: 50%;
|
width: 50%;
|
||||||
height: max-content;
|
height: max-content;
|
||||||
}
|
}
|
|
@ -34,33 +34,36 @@
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.form-row {
|
||||||
|
margin: var(--element-spacing) var(--form-spacing) 0;
|
||||||
|
border: 0;
|
||||||
|
padding: 0;
|
||||||
|
width: calc(100% - var(--form-spacing) * 2);
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-row.top {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-row.multiline {
|
||||||
|
height: max-content;
|
||||||
|
}
|
||||||
|
|
||||||
#error-message {
|
#error-message {
|
||||||
display: none;
|
display: none;
|
||||||
|
|
||||||
box-sizing: border-box;
|
line-height: 40px;
|
||||||
|
|
||||||
margin: 0 var(--form-spacing) var(--element-spacing);
|
|
||||||
width: calc(100% - (var(--form-spacing) * 2));
|
|
||||||
|
|
||||||
color: var(--error);
|
color: var(--error);
|
||||||
|
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-field {
|
.login-field {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
padding: 0 var(--element-spacing);
|
||||||
margin: 0 var(--form-spacing) var(--element-spacing);
|
font-size: 18px;
|
||||||
border: 0;
|
|
||||||
padding: 8px;
|
|
||||||
|
|
||||||
width: calc(100% - (var(--form-spacing) * 2));
|
|
||||||
height: max-content;
|
|
||||||
|
|
||||||
font-size: 20px;
|
|
||||||
|
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,41 +71,34 @@
|
||||||
background-color: var(--error);
|
background-color: var(--error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ap-button.login {
|
||||||
|
height: 40px;
|
||||||
|
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
#username,
|
#username,
|
||||||
#confirm {
|
#confirm {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#login-buttons {
|
#button-row {
|
||||||
margin: 0 calc(var(--form-spacing) - (var(--element-spacing) / 2));
|
|
||||||
width: calc(100% - (var(--form-spacing) * 2) + var(--element-spacing));
|
|
||||||
height: max-content;
|
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ap-button.login {
|
#create-user-button,
|
||||||
margin: 0 calc(var(--element-spacing) / 2);
|
#guest-login-button {
|
||||||
flex-basis: max-content;
|
width: calc(50% - var(--element-spacing) / 2);
|
||||||
}
|
|
||||||
|
|
||||||
#create-user-button {
|
|
||||||
flex-grow: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#login-button {
|
#login-button {
|
||||||
flex-grow: 1;
|
width: calc(100% - var(--form-spacing * 2))
|
||||||
}
|
}
|
||||||
|
|
||||||
#warning {
|
#warning {
|
||||||
box-sizing: border-box;
|
|
||||||
|
|
||||||
padding: 0 var(--form-spacing);
|
|
||||||
|
|
||||||
color: var(--foreground);
|
color: var(--foreground);
|
||||||
|
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
}
|
}
|
|
@ -1,16 +1,20 @@
|
||||||
:root {
|
:root {
|
||||||
--background-dark: #111115;
|
--background-dark: #111117;
|
||||||
--background: #22222A;
|
--background: #22222A;
|
||||||
--foreground-dark: #AAAAAA;
|
--foreground-dark: #AAAAAA;
|
||||||
--foreground: #EEEEEE;
|
--foreground: #EEEEEE;
|
||||||
--distinction: #44444F;
|
--distinction: #44444C;
|
||||||
|
|
||||||
--accent-value0: #500000;
|
--accent-hue: 0;
|
||||||
--accent-value1: #800000;
|
|
||||||
--accent-value2: #A00000;
|
|
||||||
--accent-value3: #D02020;
|
|
||||||
--accent-value4: #FA7575;
|
|
||||||
|
|
||||||
--selected-accent0: #0066CC;
|
--accent-value0: hsl(var(--accent-hue), 100%, 16%);
|
||||||
|
--accent-value1: hsl(var(--accent-hue), 100%, 25%);
|
||||||
|
--accent-value2: hsl(var(--accent-hue), 100%, 31%);
|
||||||
|
--accent-value3: hsl(var(--accent-hue), 73%, 47%);
|
||||||
|
--accent-value4: hsl(var(--accent-hue), 83%, 57%);
|
||||||
|
--accent-value5: hsl(var(--accent-hue), 93%, 72%);
|
||||||
|
--accent-pure: hsl(var(--accent-hue), 100%, 50%);
|
||||||
|
|
||||||
|
--selected-accent0: #2266CC;
|
||||||
--selected-accent1: #3388FF;
|
--selected-accent1: #3388FF;
|
||||||
}
|
}
|
|
@ -1,11 +1,17 @@
|
||||||
<div class="list-page-search">
|
<div class="page-subsection">
|
||||||
|
<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..." achievement-name="achievement-search" />
|
<input id="achievement-search" type="text" placeholder="Name, Keyword, etc..." 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">
|
||||||
|
<div class="page-subsection-wrapper">
|
||||||
|
<div class="page-subheader">
|
||||||
<p class="page-subheader-text">Filters</p>
|
<p class="page-subheader-text">Filters</p>
|
||||||
<div class="page-subheader-separator"></div>
|
<div class="page-subheader-separator"></div>
|
||||||
|
</div>
|
||||||
|
<div class="list-page-filter-chunk page-subsection-chunk">
|
||||||
|
<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">From Games Owned</p>
|
||||||
|
@ -19,7 +25,11 @@
|
||||||
<p class="list-page-filter-name">Completed</p>
|
<p class="list-page-filter-name">Completed</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="list-page-list-partition">
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="list-page-list-partition page-subsection-wrapper">
|
||||||
|
<div class="page-subsection-chunk">
|
||||||
<div class="list-page-list">
|
<div class="list-page-list">
|
||||||
<div class="list-page-header">
|
<div class="list-page-header">
|
||||||
<p class="list-page-entry-icon"></p>
|
<p class="list-page-entry-icon"></p>
|
||||||
|
@ -30,11 +40,15 @@
|
||||||
<template data-template="achievements-page-list: List<Basic>">
|
<template data-template="achievements-page-list: List<Basic>">
|
||||||
<div class="list-page-entry">
|
<div class="list-page-entry">
|
||||||
<img class="list-page-entry-icon" src="res/dummy_achievement.png" alt="Achievement Thumbnail"></img>
|
<img class="list-page-entry-icon" src="res/dummy_achievement.png" alt="Achievement Thumbnail"></img>
|
||||||
|
<div class="list-page-entry-text-section">
|
||||||
<p class="list-page-entry-text achievement-name">${achievement-name}</p>
|
<p class="list-page-entry-text achievement-name">${achievement-name}</p>
|
||||||
<p class="list-page-entry-text achievement-description">${achievement-description}</p>
|
<p class="list-page-entry-text achievement-description">${achievement-description}</p>
|
||||||
<p class="list-page-entry-text achievement-stages">${stages}</p>
|
<p class="list-page-entry-text achievement-stages">${stages}</p>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
|
@ -1,17 +1,27 @@
|
||||||
<div class="list-page-search">
|
<div class="page-subsection">
|
||||||
|
<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" />
|
<input id="game-search" type="text" placeholder="Name, Keyword, etc..." 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">
|
||||||
|
<div class="page-subsection-wrapper">
|
||||||
|
<div class="page-subheader">
|
||||||
<p class="page-subheader-text">Filters</p>
|
<p class="page-subheader-text">Filters</p>
|
||||||
<div class="page-subheader-separator"></div>
|
<div class="page-subheader-separator"></div>
|
||||||
|
</div>
|
||||||
|
<div class="list-page-filter-chunk page-subsection-chunk">
|
||||||
|
<div class="page-subsection-wrapper">
|
||||||
<div id="games-owned-filter" class="list-page-filter">
|
<div id="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">Games Owned</p>
|
<p class="list-page-filter-name">Games Owned</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="list-page-list-partition">
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="list-page-list-partition page-subsection-wrapper">
|
||||||
|
<div class="page-subsection-chunk">
|
||||||
<div class="list-page-list">
|
<div class="list-page-list">
|
||||||
<div class="list-page-header">
|
<div class="list-page-header">
|
||||||
<p class="list-page-entry-icon"></p>
|
<p class="list-page-entry-icon"></p>
|
||||||
|
@ -50,4 +60,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
|
@ -1,37 +1,37 @@
|
||||||
<div id="profile-section-1">
|
<div id="profile-section-1">
|
||||||
<div id="profile-info">
|
<div id="profile-info" class="page-subsection">
|
||||||
<img id="profile-info-pfp" src="res/temp_pfp.png" alt="User's Profile Pictuer" />
|
<div class="page-subsection-wrapper">
|
||||||
|
<div class="page-subheader">
|
||||||
|
<p id="profile-info-name" class="page-subheader-text">Jane Doe</p>
|
||||||
<div class="page-subheader-separator"></div>
|
<div class="page-subheader-separator"></div>
|
||||||
<p id="profile-info-name">Jane Doe</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div id="profile-platforms">
|
<div id="profile-info-pfp-border" class="page-subsection-chunk">
|
||||||
|
<div id="profile-info-pfp-vignette">
|
||||||
|
<img id="profile-info-pfp-img" src="res/guest_pfp.png" alt="User's Profile Pictuer" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="profile-platforms" class="page-subsection">
|
||||||
|
<div class="page-subsection-wrapper">
|
||||||
|
<div class="page-subheader">
|
||||||
<p class="page-subheader-text">Platforms</p>
|
<p class="page-subheader-text">Platforms</p>
|
||||||
<div class="page-subheader-separator"></div>
|
<div class="page-subheader-separator"></div>
|
||||||
<div class="profile-list">
|
</div>
|
||||||
<div class="profile-entry accented">
|
<div class="profile-list page-subsection-chunk">
|
||||||
<div class="profile-entry-left">
|
<div class="profile-entry accented top">
|
||||||
<img class="profile-entry-icon" src="res/steam.png" alt="Steam Logo" />
|
<img class="profile-entry-icon" src="res/steam.png" alt="Steam Logo" />
|
||||||
<p class="profile-entry-text">Steam</p>
|
<p class="profile-entry-text platform-name">Steam</p>
|
||||||
</div>
|
|
||||||
<div class="profile-entry-right">
|
|
||||||
<p class="profile-entry-text accented">Connected</p>
|
<p class="profile-entry-text accented">Connected</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div class="profile-entry">
|
<div class="profile-entry">
|
||||||
<div class="profile-entry-left">
|
|
||||||
<img class="profile-entry-icon" src="res/xbox.png" alt="Xbox Logo" />
|
<img class="profile-entry-icon" src="res/xbox.png" alt="Xbox Logo" />
|
||||||
<p class="profile-entry-text">Xbox Live</p>
|
<p class="profile-entry-text platform-name">Xbox Live</p>
|
||||||
</div>
|
|
||||||
<div class="profile-entry-right">
|
|
||||||
<p class="profile-entry-text accented">Connected</p>
|
<p class="profile-entry-text accented">Connected</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div class="profile-entry">
|
<div class="profile-entry">
|
||||||
<div class="profile-entry-left">
|
|
||||||
<img class="profile-entry-icon" src="res/psn.png" alt="PSN Logo" />
|
<img class="profile-entry-icon" src="res/psn.png" alt="PSN Logo" />
|
||||||
<p class="profile-entry-text">PSN</p>
|
<p class="profile-entry-text platform-name">PSN</p>
|
||||||
</div>
|
|
||||||
<div class="profile-entry-right">
|
|
||||||
<p class="profile-entry-text accented">Connected</p>
|
<p class="profile-entry-text accented">Connected</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -39,109 +39,130 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="profile-section-2">
|
<div id="profile-section-2">
|
||||||
<div id="profile-games">
|
<div id="profile-games" class="page-subsection">
|
||||||
|
<div class="page-subsection-wrapper">
|
||||||
|
<div 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 class="profile-list">
|
|
||||||
<div class="profile-entry game">
|
|
||||||
<div class="profile-entry-left">
|
|
||||||
<img class="profile-entry-icon" src="res/dummy_game.png" alt="Some Game Icon" />
|
|
||||||
<p class="profile-entry-text">Latin</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="profile-list page-subsection-chunk">
|
||||||
|
<div class="profile-entry game top">
|
||||||
|
<img class="profile-entry-icon" src="res/dummy_game.png" alt="Some Game Icon" />
|
||||||
|
<p class="profile-entry-text game-name">Latin</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="profile-entry game">
|
<div class="profile-entry game">
|
||||||
<div class="profile-entry-left">
|
|
||||||
<img class="profile-entry-icon" src="res/dummy_game.png" alt="Some Game Icon" />
|
<img class="profile-entry-icon" src="res/dummy_game.png" alt="Some Game Icon" />
|
||||||
<p class="profile-entry-text">Latin</p>
|
<p class="profile-entry-text game-name">Latin</p>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="profile-entry game">
|
<div class="profile-entry game">
|
||||||
<div class="profile-entry-left">
|
|
||||||
<img class="profile-entry-icon" src="res/dummy_game.png" alt="Some Game Icon" />
|
<img class="profile-entry-icon" src="res/dummy_game.png" alt="Some Game Icon" />
|
||||||
<p class="profile-entry-text">Latin</p>
|
<p class="profile-entry-text game-name">Latin</p>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="profile-entry game">
|
<div class="profile-entry game">
|
||||||
<div class="profile-entry-left">
|
|
||||||
<img class="profile-entry-icon" src="res/dummy_game.png" alt="Some Game Icon" />
|
<img class="profile-entry-icon" src="res/dummy_game.png" alt="Some Game Icon" />
|
||||||
<p class="profile-entry-text">Latin</p>
|
<p class="profile-entry-text game-name">Latin</p>
|
||||||
|
</div>
|
||||||
|
<div class="profile-entry game">
|
||||||
|
<img class="profile-entry-icon" src="res/dummy_game.png" alt="Some Game Icon" />
|
||||||
|
<p class="profile-entry-text game-name">Latin</p>
|
||||||
|
</div>
|
||||||
|
<div class="profile-entry game">
|
||||||
|
<img class="profile-entry-icon" src="res/dummy_game.png" alt="Some Game Icon" />
|
||||||
|
<p class="profile-entry-text game-name">Latin</p>
|
||||||
|
</div>
|
||||||
|
<div class="profile-entry game">
|
||||||
|
<img class="profile-entry-icon" src="res/dummy_game.png" alt="Some Game Icon" />
|
||||||
|
<p class="profile-entry-text game-name">Latin</p>
|
||||||
|
</div>
|
||||||
|
<div class="profile-entry game">
|
||||||
|
<img class="profile-entry-icon" src="res/dummy_game.png" alt="Some Game Icon" />
|
||||||
|
<p class="profile-entry-text game-name">Latin</p>
|
||||||
|
</div>
|
||||||
|
<div class="profile-entry game">
|
||||||
|
<img class="profile-entry-icon" src="res/dummy_game.png" alt="Some Game Icon" />
|
||||||
|
<p class="profile-entry-text game-name">Latin</p>
|
||||||
|
</div>
|
||||||
|
<div class="profile-entry game">
|
||||||
|
<img class="profile-entry-icon" src="res/dummy_game.png" alt="Some Game Icon" />
|
||||||
|
<p class="profile-entry-text game-name">Latin</p>
|
||||||
|
</div>
|
||||||
|
<div class="profile-entry game">
|
||||||
|
<img class="profile-entry-icon" src="res/dummy_game.png" alt="Some Game Icon" />
|
||||||
|
<p class="profile-entry-text game-name">Latin</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="profile-achievements">
|
<div id="profile-achievements" class="page-subsection">
|
||||||
|
<div class="page-subsection-wrapper">
|
||||||
|
<div 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 class="profile-list">
|
</div>
|
||||||
<div class="profile-entry accented">
|
<div class="profile-list page-subsection-chunk">
|
||||||
<div class="profile-entry-left">
|
<div class="profile-entry accented top">
|
||||||
<img class="profile-entry-icon" src="res/dummy_achievement.png" alt="Some Achievement Icon" />
|
<img class="profile-entry-icon" src="res/dummy_achievement.png" alt="Some Achievement Icon" />
|
||||||
<p class="profile-entry-text">Lorem Ipsum</p>
|
<p class="profile-entry-text achievement-name">Lorem Ipsum</p>
|
||||||
</div>
|
|
||||||
<div class="profile-entry-right">
|
|
||||||
<p class="profile-entry-text accented">Completed</p>
|
<p class="profile-entry-text accented">Completed</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div class="profile-entry accented">
|
<div class="profile-entry accented">
|
||||||
<div class="profile-entry-left">
|
|
||||||
<img class="profile-entry-icon" src="res/dummy_achievement.png" alt="Some Achievement Icon" />
|
<img class="profile-entry-icon" src="res/dummy_achievement.png" alt="Some Achievement Icon" />
|
||||||
<p class="profile-entry-text">Lorem Ipsum</p>
|
<p class="profile-entry-text achievement-name">Lorem Ipsum</p>
|
||||||
</div>
|
|
||||||
<div class="profile-entry-right">
|
|
||||||
<p class="profile-entry-text accented">Completed</p>
|
<p class="profile-entry-text accented">Completed</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div class="profile-entry accented">
|
<div class="profile-entry accented">
|
||||||
<div class="profile-entry-left">
|
|
||||||
<img class="profile-entry-icon" src="res/dummy_achievement.png" alt="Some Achievement Icon" />
|
<img class="profile-entry-icon" src="res/dummy_achievement.png" alt="Some Achievement Icon" />
|
||||||
<p class="profile-entry-text">Lorem Ipsum</p>
|
<p class="profile-entry-text achievement-name">Lorem Ipsum</p>
|
||||||
</div>
|
|
||||||
<div class="profile-entry-right">
|
|
||||||
<p class="profile-entry-text accented">Completed</p>
|
<p class="profile-entry-text accented">Completed</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="profile-entry accented">
|
||||||
<div class="profile-entry">
|
|
||||||
<div class="profile-entry-left">
|
|
||||||
<img class="profile-entry-icon" src="res/dummy_achievement.png" alt="Some Achievement Icon" />
|
<img class="profile-entry-icon" src="res/dummy_achievement.png" alt="Some Achievement Icon" />
|
||||||
<p class="profile-entry-text">Lorem Ipsum</p>
|
<p class="profile-entry-text achievement-name">Lorem Ipsum</p>
|
||||||
</div>
|
|
||||||
<div class="profile-entry-right">
|
|
||||||
<p class="profile-entry-text accented">Completed</p>
|
<p class="profile-entry-text accented">Completed</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="profile-entry accented">
|
||||||
<div class="profile-entry">
|
|
||||||
<div class="profile-entry-left">
|
|
||||||
<img class="profile-entry-icon" src="res/dummy_achievement.png" alt="Some Achievement Icon" />
|
<img class="profile-entry-icon" src="res/dummy_achievement.png" alt="Some Achievement Icon" />
|
||||||
<p class="profile-entry-text">Lorem Ipsum</p>
|
<p class="profile-entry-text achievement-name">Lorem Ipsum</p>
|
||||||
</div>
|
|
||||||
<div class="profile-entry-right">
|
|
||||||
<p class="profile-entry-text accented">Completed</p>
|
<p class="profile-entry-text accented">Completed</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="profile-entry accented">
|
||||||
<div class="profile-entry">
|
|
||||||
<div class="profile-entry-left">
|
|
||||||
<img class="profile-entry-icon" src="res/dummy_achievement.png" alt="Some Achievement Icon" />
|
<img class="profile-entry-icon" src="res/dummy_achievement.png" alt="Some Achievement Icon" />
|
||||||
<p class="profile-entry-text">Lorem Ipsum</p>
|
<p class="profile-entry-text achievement-name">Lorem Ipsum</p>
|
||||||
</div>
|
|
||||||
<div class="profile-entry-right">
|
|
||||||
<p class="profile-entry-text accented">Completed</p>
|
<p class="profile-entry-text accented">Completed</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="profile-entry accented">
|
||||||
<div class="profile-entry">
|
|
||||||
<div class="profile-entry-left">
|
|
||||||
<img class="profile-entry-icon" src="res/dummy_achievement.png" alt="Some Achievement Icon" />
|
<img class="profile-entry-icon" src="res/dummy_achievement.png" alt="Some Achievement Icon" />
|
||||||
<p class="profile-entry-text">Lorem Ipsum</p>
|
<p class="profile-entry-text achievement-name">Lorem Ipsum</p>
|
||||||
</div>
|
|
||||||
<div class="profile-entry-right">
|
|
||||||
<p class="profile-entry-text accented">Completed</p>
|
<p class="profile-entry-text accented">Completed</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="profile-entry">
|
||||||
|
<img class="profile-entry-icon" src="res/dummy_achievement.png" alt="Some Achievement Icon" />
|
||||||
|
<p class="profile-entry-text achievement-name">Lorem Ipsum</p>
|
||||||
|
<p class="profile-entry-text accented">Completed</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="profile-entry">
|
<div class="profile-entry">
|
||||||
<div class="profile-entry-left">
|
|
||||||
<img class="profile-entry-icon" src="res/dummy_achievement.png" alt="Some Achievement Icon" />
|
<img class="profile-entry-icon" src="res/dummy_achievement.png" alt="Some Achievement Icon" />
|
||||||
<p class="profile-entry-text">Lorem Ipsum</p>
|
<p class="profile-entry-text achievement-name">Lorem Ipsum</p>
|
||||||
|
<p class="profile-entry-text accented">Completed</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="profile-entry-right">
|
<div class="profile-entry">
|
||||||
|
<img class="profile-entry-icon" src="res/dummy_achievement.png" alt="Some Achievement Icon" />
|
||||||
|
<p class="profile-entry-text achievement-name">Lorem Ipsum</p>
|
||||||
|
<p class="profile-entry-text accented">Completed</p>
|
||||||
|
</div>
|
||||||
|
<div class="profile-entry">
|
||||||
|
<img class="profile-entry-icon" src="res/dummy_achievement.png" alt="Some Achievement Icon" />
|
||||||
|
<p class="profile-entry-text achievement-name">Lorem Ipsum</p>
|
||||||
|
<p class="profile-entry-text accented">Completed</p>
|
||||||
|
</div>
|
||||||
|
<div class="profile-entry">
|
||||||
|
<img class="profile-entry-icon" src="res/dummy_achievement.png" alt="Some Achievement Icon" />
|
||||||
|
<p class="profile-entry-text achievement-name">Lorem Ipsum</p>
|
||||||
|
<p class="profile-entry-text accented">Completed</p>
|
||||||
|
</div>
|
||||||
|
<div class="profile-entry">
|
||||||
|
<img class="profile-entry-icon" src="res/dummy_achievement.png" alt="Some Achievement Icon" />
|
||||||
|
<p class="profile-entry-text achievement-name">Lorem Ipsum</p>
|
||||||
<p class="profile-entry-text accented">Completed</p>
|
<p class="profile-entry-text accented">Completed</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -32,7 +32,8 @@ CREATE TABLE [User] (
|
||||||
Email VARCHAR(254) NOT NULL,
|
Email VARCHAR(254) NOT NULL,
|
||||||
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
|
||||||
PRIMARY KEY(ID)
|
PRIMARY KEY(ID)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
Binary file not shown.
4
sql/GetUserLogin.sql
Normal file
4
sql/GetUserLogin.sql
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
CREATE PROCEDURE GetUserLogin(
|
||||||
|
@email VARCHAR(254)
|
||||||
|
) AS
|
||||||
|
SELECT Id, Salt, [Password] FROM [User] WHERE Email = @email
|
1187
tmp/unknown.ai
Normal file
1187
tmp/unknown.ai
Normal file
File diff suppressed because one or more lines are too long
BIN
tmp/unknown.png
Normal file
BIN
tmp/unknown.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 92 KiB |
Loading…
Reference in a new issue