2 Commits
Author SHA1 Message Date
Peter 43430cfbef Merge remote-tracking branch 'origin/bans' into bans
# Conflicts:
#	frontend/src/app/app.routes.ts
2025-07-06 11:13:59 +02:00
Peter 174ed834ca Added pages and fitting content for community, nickgenerator annd nicknames 2025-05-30 23:07:42 +02:00
189 changed files with 424 additions and 2826 deletions
@@ -1,36 +0,0 @@
package com.alttd.altitudeweb.config;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
import java.io.IOException;
@Slf4j
@Component
public class SecurityAuthFailureHandler implements AccessDeniedHandler, AuthenticationEntryPoint {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response,
AccessDeniedException accessDeniedException) throws IOException {
log.warn("Access denied: User '{}' attempted to access '{}' without proper permissions",
request.getUserPrincipal() != null ? request.getUserPrincipal().getName() : "unknown",
request.getRequestURI());
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Access Denied");
}
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException {
log.warn("Authentication failure: Unauthenticated user attempted to access secured endpoint '{}'",
request.getRequestURI());
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authentication Required");
}
}
@@ -31,34 +31,20 @@ import java.security.interfaces.RSAPublicKey;
public class SecurityConfig { public class SecurityConfig {
private final KeyPairService keyPairService; private final KeyPairService keyPairService;
private final SecurityAuthFailureHandler securityAuthFailureHandler;
@Bean @Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http return http
.authorizeHttpRequests( .authorizeHttpRequests(auth -> auth
auth -> auth .requestMatchers("/login/userLogin/**", "/login/requestNewUserLogin/**").permitAll()
.requestMatchers("/form/**").hasAuthority(PermissionClaimDto.USER.getValue()) .requestMatchers("/team/**", "/history/**").permitAll()
.requestMatchers("/head_mod/**").hasAuthority(PermissionClaimDto.HEAD_MOD.getValue()) .requestMatchers("/form/**").hasAuthority(PermissionClaimDto.USER.getValue())
.requestMatchers("/particles/**").hasAuthority(PermissionClaimDto.HEAD_MOD.getValue()) .requestMatchers("/head_mod/**").hasAuthority(PermissionClaimDto.HEAD_MOD.getValue())
.requestMatchers("/files/save/**").hasAuthority(PermissionClaimDto.HEAD_MOD.getValue()) .anyRequest().authenticated()
.anyRequest().permitAll() )
) .oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()))
.oauth2ResourceServer( .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
oauth2 -> oauth2 .build();
.jwt(Customizer.withDefaults())
.authenticationEntryPoint(securityAuthFailureHandler)
.accessDeniedHandler(securityAuthFailureHandler)
)
.exceptionHandling(
ex -> ex
.authenticationEntryPoint(securityAuthFailureHandler)
.accessDeniedHandler(securityAuthFailureHandler)
)
.sessionManagement(
session -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.build();
} }
@Bean @Bean
@@ -83,6 +83,8 @@ public class LoginController implements LoginApi {
@RateLimit(limit = 5, timeValue = 1, timeUnit = TimeUnit.MINUTES, key = "login") @RateLimit(limit = 5, timeValue = 1, timeUnit = TimeUnit.MINUTES, key = "login")
@Override @Override
public ResponseEntity<String> login(String code) { public ResponseEntity<String> login(String code) {
CacheEntry cacheEntry1 = new CacheEntry(UUID.fromString("55e46bc3-2a29-4c53-850f-dbd944dc5c5f"), Instant.now().plusSeconds(TimeUnit.DAYS.toSeconds(1)));
cache.put("23232323", cacheEntry1);
if (code == null) { if (code == null) {
return ResponseEntity.badRequest().build(); return ResponseEntity.badRequest().build();
} }
@@ -132,12 +134,12 @@ public class LoginController implements LoginApi {
Instant now = Instant.now(); Instant now = Instant.now();
//TODO make a JWT for renewing and one for storing permissions for a session (expiry 1 hour) //TODO make a JWT for renewing and one for storing permissions for a session (expiry 1 hour)
Instant expiryTime = now.plusSeconds(TimeUnit.DAYS.toSeconds(30)); Instant expiryTime = now.plusSeconds(TimeUnit.DAYS.toSeconds(30));
CompletableFuture<Optional<PrivilegedUser>> privilegedUserCompletableFuture = new CompletableFuture<>(); CompletableFuture<PrivilegedUser> privilegedUserCompletableFuture = new CompletableFuture<>();
List<PermissionClaimDto> claimList = new ArrayList<>(); List<PermissionClaimDto> claimList = new ArrayList<>();
Connection.getConnection(Databases.DEFAULT) Connection.getConnection(Databases.DEFAULT)
.runQuery(sqlSession -> { .runQuery(sqlSession -> {
try { try {
Optional<PrivilegedUser> privilegedUser = sqlSession.getMapper(PrivilegedUserMapper.class) PrivilegedUser privilegedUser = sqlSession.getMapper(PrivilegedUserMapper.class)
.getUserByUuid(uuid.toString()); .getUserByUuid(uuid.toString());
privilegedUserCompletableFuture.complete(privilegedUser); privilegedUserCompletableFuture.complete(privilegedUser);
@@ -146,15 +148,17 @@ public class LoginController implements LoginApi {
privilegedUserCompletableFuture.completeExceptionally(e); privilegedUserCompletableFuture.completeExceptionally(e);
} }
}); });
Optional<PrivilegedUser> privilegedUser = privilegedUserCompletableFuture.join(); PrivilegedUser privilegedUser = privilegedUserCompletableFuture.join();
claimList.add(PermissionClaimDto.USER); claimList.add(PermissionClaimDto.USER);
privilegedUser.ifPresent(user -> user.getPermissions().forEach(permission -> { if (privilegedUser != null) {
try { privilegedUser.getPermissions().forEach(permission -> {
claimList.add(PermissionClaimDto.valueOf(permission)); try {
} catch (IllegalArgumentException e) { claimList.add(PermissionClaimDto.valueOf(permission));
log.warn("Received invalid permission claim: {}", permission); } catch (IllegalArgumentException e) {
} log.warn("Received invalid permission claim: {}", permission);
})); }
});
}
JwtClaimsSet claims = JwtClaimsSet.builder() JwtClaimsSet claims = JwtClaimsSet.builder()
.issuer("altitudeweb") .issuer("altitudeweb")
.claim("authorities", claimList.stream().map(PermissionClaimDto::getValue).toList()) .claim("authorities", claimList.stream().map(PermissionClaimDto::getValue).toList())
@@ -1,176 +0,0 @@
package com.alttd.altitudeweb.controllers.particles;
import com.alttd.altitudeweb.api.ParticlesApi;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
@Slf4j
@RequiredArgsConstructor
@RestController
public class ParticleController implements ParticlesApi {
@Value("${login.secret:#{null}}")
private String loginSecret;
@Value("${particles.file_path}")
private String particlesFilePath;
@Value("${notification.server.url:http://localhost:8080}")
private String notificationServerUrl;
@Override
public ResponseEntity<Resource> downloadFile(String authorization, String filename) throws Exception {
if (authorization == null || !authorization.equals(loginSecret)) {
return ResponseEntity.status(401).build();
}
File file = new File(particlesFilePath);
if (!file.exists() || !file.isDirectory()) {
log.error("Particles file path {} is not a directory, not downloading particles file", particlesFilePath);
return ResponseEntity.status(404).build();
}
File targetFile = new File(file, filename);
return getFileForDownload(targetFile, filename);
}
@Override
public ResponseEntity<Resource> downloadFileForUser(String authorization, String uuid, String filename) throws Exception {
if (authorization == null || !authorization.equals(loginSecret)) {
return ResponseEntity.status(401).build();
}
File file = new File(particlesFilePath);
if (!file.exists() || !file.isDirectory()) {
log.error("Particles file path {} is not a directory, not downloading particles user file", particlesFilePath);
return ResponseEntity.status(404).build();
}
File targetDir = new File(file, uuid);
if (targetDir.exists()) {
return getFileForDownload(targetDir, filename);
} else {
log.warn("User {} does not have a directory for particles files", uuid);
return ResponseEntity.notFound().build();
}
}
private ResponseEntity<Resource> getFileForDownload(File file, String filename) {
File targetFile = new File(file, filename);
if (!targetFile.exists()) {
log.warn("Particles file {} does not exist", targetFile.getAbsolutePath());
return ResponseEntity.notFound().build();
}
if (!targetFile.isFile()) {
log.warn("Particles file {} is not a file", targetFile.getAbsolutePath());
return ResponseEntity.status(404).build();
}
try {
Path path = targetFile.toPath();
ByteArrayResource resource = new ByteArrayResource(Files.readAllBytes(path));
return ResponseEntity.ok()
.contentLength(targetFile.length())
.contentType(MediaType.APPLICATION_JSON)
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + filename + "\"")
.body(resource);
} catch (IOException e) {
log.error("Failed to read particles file {}: {}", targetFile.getAbsolutePath(), e.getMessage(), e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
@Override
public ResponseEntity<Void> saveFile(String filename, MultipartFile content) throws Exception {
File file = new File(particlesFilePath);
if (!file.exists() || !file.isDirectory()) {
log.error("Particles file path {} is not a directory, not saving particles file", particlesFilePath);
return ResponseEntity.status(404).build();
}
ResponseEntity<Void> voidResponseEntity = writeContentToFile(file, filename, content);
notifyServerOfFileUpload(filename);
return voidResponseEntity;
}
@Override
public ResponseEntity<Void> saveFileForUser(String uuid, String filename, MultipartFile content) throws Exception {
File file = new File(particlesFilePath);
if (!file.exists() || !file.isDirectory()) {
log.error("Particles file path {} is not a directory, not saving particles user file", particlesFilePath);
return ResponseEntity.status(404).build();
}
File targetDir = new File(file, uuid);
if (!file.exists()) {
log.debug("Creating particles directory {}", targetDir.getAbsolutePath());
if (targetDir.mkdirs()) {
log.info("Created particles user directory {}", targetDir.getAbsolutePath());
}
}
ResponseEntity<Void> voidResponseEntity = writeContentToFile(file, filename, content);
notifyServerOfFileUpload(uuid, filename);
return voidResponseEntity;
}
private void notifyServerOfFileUpload(String filename) {
String notificationUrl = String.format("%s/notify/%s.json", notificationServerUrl, filename);
sendNotification(notificationUrl, String.format("file upload: %s", filename));
}
private void notifyServerOfFileUpload(String uuid, String filename) {
String notificationUrl = String.format("%s/notify/%s/%s.json", notificationServerUrl, uuid, filename);
sendNotification(notificationUrl, String.format("file upload for user %s: %s", uuid, filename));
}
private void sendNotification(String notificationUrl, String logDescription) {
try {
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate.getForEntity(notificationUrl, String.class);
if (response.getStatusCode().is2xxSuccessful()) {
log.info("Successfully notified server of {}", logDescription);
} else {
log.warn("Failed to notify server of {}, status: {}",
logDescription, response.getStatusCode());
}
} catch (Exception e) {
log.error("Error notifying server of {}", logDescription, e);
}
}
private ResponseEntity<Void> writeContentToFile(File dir, String filename, MultipartFile content) {
File targetFile = new File(dir, filename);
if (!Files.isWritable(targetFile.toPath())) {
log.error("Particles file {} is not writable", targetFile.getAbsolutePath());
return ResponseEntity.status(403).build();
}
if (targetFile.exists()) {
log.warn("Overwriting existing particles file {}", targetFile.getAbsolutePath());
}
try {
content.transferTo(targetFile);
} catch (Exception e) {
log.error("Failed to write particles file {}", targetFile.getAbsolutePath(), e);
return ResponseEntity.status(500).build();
}
return ResponseEntity.ok().build();
}
}
@@ -6,6 +6,4 @@ database.user=${DB_USER:root}
database.password=${DB_PASSWORD:root} database.password=${DB_PASSWORD:root}
cors.allowed-origins=${CORS:https://alttd.com} cors.allowed-origins=${CORS:https://alttd.com}
login.secret=${LOGIN_SECRET:SET_TOKEN} login.secret=${LOGIN_SECRET:SET_TOKEN}
particles.file_path=${user.home}/.altitudeweb/particles
notification.server.url=${SERVER_IP:10.0.0.107}:${SERVER_PORT:8080}
logging.level.com.alttd.altitudeweb=INFO logging.level.com.alttd.altitudeweb=INFO
@@ -1,10 +1,8 @@
package com.alttd.altitudeweb.database.web_db; package com.alttd.altitudeweb.database.web_db;
import org.apache.ibatis.annotations.*; import org.apache.ibatis.annotations.*;
import org.jetbrains.annotations.Nullable;
import java.util.List; import java.util.List;
import java.util.Optional;
public interface PrivilegedUserMapper { public interface PrivilegedUserMapper {
@@ -25,7 +23,7 @@ public interface PrivilegedUserMapper {
@Result(property = "permissions", column = "id", javaType = List.class, @Result(property = "permissions", column = "id", javaType = List.class,
many = @Many(select = "getPermissionsForUser")) many = @Many(select = "getPermissionsForUser"))
}) })
Optional<PrivilegedUser> getUserByUuid(@Param("uuid") String uuid); PrivilegedUser getUserByUuid(@Param("uuid") String uuid);
/** /**
* Retrieves all privileged users with their permissions * Retrieves all privileged users with their permissions
-2
View File
@@ -22,10 +22,8 @@
"@angular/platform-browser": "^19.2.0", "@angular/platform-browser": "^19.2.0",
"@angular/platform-browser-dynamic": "^19.2.0", "@angular/platform-browser-dynamic": "^19.2.0",
"@angular/router": "^19.2.0", "@angular/router": "^19.2.0",
"@types/three": "^0.177.0",
"ngx-cookie-service": "^19.1.2", "ngx-cookie-service": "^19.1.2",
"rxjs": "~7.8.0", "rxjs": "~7.8.0",
"three": "^0.177.0",
"tslib": "^2.3.0", "tslib": "^2.3.0",
"zone.js": "~0.15.0" "zone.js": "~0.15.0"
}, },
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

@@ -1,14 +1,16 @@
import {Component} from '@angular/core'; import {Component} from '@angular/core';
import {ScrollService} from '@services/scroll.service'; import {ScrollService} from '../scroll/scroll.service';
import {CommonModule} from '@angular/common'; import {CommonModule} from '@angular/common';
import {HeaderComponent} from '@header/header.component'; import {HeaderComponent} from '../header/header.component';
import {RemoveTrailingPeriodPipe} from "../util/RemoveTrailingPeriodPipe";
@Component({ @Component({
selector: 'app-about', selector: 'app-about',
standalone: true, standalone: true,
imports: [ imports: [
CommonModule, CommonModule,
HeaderComponent HeaderComponent,
RemoveTrailingPeriodPipe
], ],
templateUrl: './about.component.html', templateUrl: './about.component.html',
styleUrl: './about.component.scss' styleUrl: './about.component.scss'
+4 -4
View File
@@ -1,8 +1,8 @@
import {Component, OnInit} from '@angular/core'; import {Component, OnInit} from '@angular/core';
import {Meta, Title} from '@angular/platform-browser'; import {Meta, Title} from '@angular/platform-browser';
import {ALTITUDE_VERSION} from '@custom-types/constant'; import {ALTITUDE_VERSION} from './constant';
import {Router, RouterOutlet} from '@angular/router'; import {Router, RouterOutlet} from '@angular/router';
import {FooterComponent} from '@pages/footer/footer/footer.component'; import {FooterComponent} from './footer/footer.component';
@Component({ @Component({
standalone: true, standalone: true,
@@ -10,8 +10,8 @@ import {FooterComponent} from '@pages/footer/footer/footer.component';
templateUrl: './app.component.html', templateUrl: './app.component.html',
styleUrl: './app.component.scss', styleUrl: './app.component.scss',
imports: [ imports: [
RouterOutlet, FooterComponent,
FooterComponent RouterOutlet
] ]
}) })
export class AppComponent implements OnInit { export class AppComponent implements OnInit {
+29 -31
View File
@@ -3,110 +3,108 @@ import {Routes} from '@angular/router';
export const routes: Routes = [ export const routes: Routes = [
{ {
path: '', path: '',
loadComponent: () => import('./pages/home/home.component').then(m => m.HomeComponent) loadComponent: () => import('./home/home.component').then(m => m.HomeComponent)
},
{
path: 'particles',
loadComponent: () => import('./pages/particles/particles.component').then(m => m.ParticlesComponent)
}, },
{ {
path: 'map', path: 'map',
loadComponent: () => import('./pages/features/map/map.component').then(m => m.MapComponent) loadComponent: () => import('./map/map.component').then(m => m.MapComponent)
}, },
{ {
path: 'rules', path: 'rules',
loadComponent: () => import('./pages/reference/rules/rules.component').then(m => m.RulesComponent) loadComponent: () => import('./rules/rules.component').then(m => m.RulesComponent)
}, },
{ {
path: 'vote', path: 'vote',
loadComponent: () => import('./pages/vote/vote.component').then(m => m.VoteComponent) loadComponent: () => import('./vote/vote.component').then(m => m.VoteComponent)
}, },
{ {
path: 'about', path: 'about',
loadComponent: () => import('./pages/altitude/about/about.component').then(m => m.AboutComponent) loadComponent: () => import('./about/about.component').then(m => m.AboutComponent)
}, },
{ {
path: 'socials', path: 'socials',
loadComponent: () => import('./pages/altitude/socials/socials.component').then(m => m.SocialsComponent) loadComponent: () => import('./socials/socials.component').then(m => m.SocialsComponent)
}, },
{ {
path: 'team', path: 'team',
loadComponent: () => import('./pages/altitude/team/team.component').then(m => m.TeamComponent) loadComponent: () => import('./team/team.component').then(m => m.TeamComponent)
}, },
{ {
path: 'birthdays', path: 'birthdays',
loadComponent: () => import('./pages/altitude/birthdays/birthdays.component').then(m => m.BirthdaysComponent) loadComponent: () => import('./birthdays/birthdays.component').then(m => m.BirthdaysComponent)
}, },
{ {
path: 'terms', path: 'terms',
loadComponent: () => import('./pages/footer/terms/terms.component').then(m => m.TermsComponent) loadComponent: () => import('./terms/terms.component').then(m => m.TermsComponent)
}, },
{ {
path: 'privacy', path: 'privacy',
loadComponent: () => import('./pages/footer/privacy/privacy.component').then(m => m.PrivacyComponent) loadComponent: () => import('./privacy/privacy.component').then(m => m.PrivacyComponent)
}, },
{ {
path: 'bans', path: 'bans',
loadComponent: () => import('./pages/reference/bans/bans.component').then(m => m.BansComponent) loadComponent: () => import('./bans/bans.component').then(m => m.BansComponent)
}, },
{ {
path: 'bans/:type/:id', path: 'bans/:type/:id',
loadComponent: () => import('./pages/reference/bans/details/details.component').then(m => m.DetailsComponent) loadComponent: () => import('./bans/details/details.component').then(m => m.DetailsComponent)
}, },
{ {
path: 'economy', path: 'economy',
loadComponent: () => import('./pages/features/economy/economy.component').then(m => m.EconomyComponent) loadComponent: () => import('./economy/economy.component').then(m => m.EconomyComponent)
}, },
{ {
path: 'claiming', path: 'claiming',
loadComponent: () => import('./pages/features/claiming/claiming.component').then(m => m.ClaimingComponent) loadComponent: () => import('./claiming/claiming.component').then(m => m.ClaimingComponent)
}, },
{ {
path: 'mypet', path: 'mypet',
loadComponent: () => import('./pages/features/mypet/mypet.component').then(m => m.MypetComponent) loadComponent: () => import('./mypet/mypet.component').then(m => m.MypetComponent)
}, },
{ {
path: 'warps', path: 'warps',
loadComponent: () => import('./pages/features/warps/warps.component').then(m => m.WarpsComponent) loadComponent: () => import('./warps/warps.component').then(m => m.WarpsComponent)
}, },
{ {
path: 'skyblock', path: 'skyblock',
loadComponent: () => import('./pages/features/skyblock/skyblock.component').then(m => m.SkyblockComponent) loadComponent: () => import('./skyblock/skyblock.component').then(m => m.SkyblockComponent)
}, },
{ {
path: 'customfeatures', path: 'customfeatures',
loadComponent: () => import('./pages/features/customfeatures/customfeatures.component').then(m => m.CustomfeaturesComponent) loadComponent: () => import('./customfeatures/customfeatures.component').then(m => m.CustomfeaturesComponent)
}, },
{ {
path: 'guide', path: 'guide',
loadComponent: () => import('./pages/reference/guide/guide.component').then(m => m.GuideComponent) loadComponent: () => import('./guide/guide.component').then(m => m.GuideComponent)
}, },
{ {
path: 'ranks', path: 'ranks',
loadComponent: () => import('./pages/reference/ranks/ranks.component').then(m => m.RanksComponent) loadComponent: () => import('./ranks/ranks.component').then(m => m.RanksComponent)
}, },
{ {
path: 'commandlist', path: 'commandlist',
loadComponent: () => import('./pages/reference/commandlist/commandlist.component').then(m => m.CommandlistComponent) loadComponent: () => import('./commandlist/commandlist.component').then(m => m.CommandlistComponent)
}, },
{ {
path: 'mapart', path: 'mapart',
loadComponent: () => import('./pages/reference/mapart/mapart.component').then(m => m.MapartComponent) loadComponent: () => import('./mapart/mapart.component').then(m => m.MapartComponent)
}, },
{ {
path: 'lag', path: 'lag',
loadComponent: () => import('./pages/reference/lag/lag.component').then(m => m.LagComponent) loadComponent: () => import('./lag/lag.component').then(m => m.LagComponent)
}, },
{ {
path: 'staffpowers', path: 'staffpowers',
loadComponent: () => import('./pages/reference/staffpowers/staffpowers.component').then(m => m.StaffpowersComponent) loadComponent: () => import('./staffpowers/staffpowers.component').then(m => m.StaffpowersComponent)
}, },
{ {
path: 'forms/:form', path: 'forms/:form',
loadComponent: () => import('./pages/forms/forms.component').then(m => m.FormsComponent) loadComponent: () => import('./forms/forms.component').then(m => m.FormsComponent)
}, },
{ {
path: 'forms', path: 'forms',
loadComponent: () => import('./pages/forms/forms.component').then(m => m.FormsComponent) loadComponent: () => import('./forms/forms.component').then(m => m.FormsComponent)
} },
]; ];
@@ -1,7 +1,7 @@
import {Component, OnInit} from '@angular/core'; import {Component, OnInit} from '@angular/core';
import {HeaderComponent} from "@header/header.component"; import {HeaderComponent} from "../header/header.component";
import {HistoryComponent} from './history/history.component'; import {HistoryComponent} from './history/history.component';
import {HistoryCount, HistoryService} from '@api'; import {HistoryCount, HistoryService} from '../../api';
import {NgClass, NgForOf, NgIf} from '@angular/common'; import {NgClass, NgForOf, NgIf} from '@angular/common';
import {FormsModule} from '@angular/forms'; import {FormsModule} from '@angular/forms';
import {catchError, map, Observable} from 'rxjs'; import {catchError, map, Observable} from 'rxjs';
@@ -1,11 +1,11 @@
import {Component, OnInit} from '@angular/core'; import {Component, OnInit} from '@angular/core';
import {HistoryService, PunishmentHistory} from '@api'; import {HistoryService, PunishmentHistory} from '../../../api';
import {NgClass, NgIf, NgOptimizedImage} from '@angular/common'; import {NgClass, NgIf, NgOptimizedImage} from '@angular/common';
import {RemoveTrailingPeriodPipe} from '@pipes/RemoveTrailingPeriodPipe'; import {RemoveTrailingPeriodPipe} from '../../util/RemoveTrailingPeriodPipe';
import {HistoryFormatService} from '../history-format.service'; import {HistoryFormatService} from '../history-format.service';
import {ActivatedRoute, RouterLink} from '@angular/router'; import {ActivatedRoute, RouterLink} from '@angular/router';
import {catchError, map} from 'rxjs'; import {catchError, map} from 'rxjs';
import {HeaderComponent} from '@header/header.component'; import {HeaderComponent} from '../../header/header.component';
@Component({ @Component({
selector: 'app-details', selector: 'app-details',
@@ -1,5 +1,5 @@
import {Injectable} from '@angular/core'; import {Injectable} from '@angular/core';
import {PunishmentHistory} from '@api'; import {PunishmentHistory} from '../../api';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
@@ -1,11 +1,11 @@
import {Component, EventEmitter, Input, OnChanges, OnInit, Output} from '@angular/core'; import {Component, EventEmitter, Input, OnChanges, OnInit, Output} from '@angular/core';
import {BASE_PATH, HistoryService, PunishmentHistory} from '@api'; import {BASE_PATH, HistoryService, PunishmentHistory} from '../../../api';
import {catchError, map, Observable, shareReplay} from 'rxjs'; import {catchError, map, Observable, shareReplay} from 'rxjs';
import {NgForOf, NgIf, NgOptimizedImage} from '@angular/common'; import {NgForOf, NgIf, NgOptimizedImage} from '@angular/common';
import {CookieService} from 'ngx-cookie-service'; import {CookieService} from 'ngx-cookie-service';
import {RemoveTrailingPeriodPipe} from '@pipes/RemoveTrailingPeriodPipe'; import {RemoveTrailingPeriodPipe} from '../../util/RemoveTrailingPeriodPipe';
import {HttpErrorResponse} from '@angular/common/http'; import {HttpErrorResponse} from '@angular/common/http';
import {environment} from '@environment'; import {environment} from '../../../environments/environment';
import {HistoryFormatService} from '../history-format.service'; import {HistoryFormatService} from '../history-format.service';
import {SearchParams} from '../search-terms'; import {SearchParams} from '../search-terms';
import {Router} from '@angular/router'; import {Router} from '@angular/router';
@@ -1,7 +1,7 @@
import {Component} from '@angular/core'; import {Component} from '@angular/core';
import {ScrollService} from '@services/scroll.service'; import {ScrollService} from '../scroll/scroll.service';
import {CommonModule} from '@angular/common'; import {CommonModule} from '@angular/common';
import {HeaderComponent} from '@header/header.component'; import {HeaderComponent} from '../header/header.component';
@Component({ @Component({
selector: 'app-birthdays', selector: 'app-birthdays',
@@ -1,5 +1,5 @@
import {Component} from '@angular/core'; import {Component} from '@angular/core';
import {HeaderComponent} from '@header/header.component'; import {HeaderComponent} from '../header/header.component';
import {NgOptimizedImage} from '@angular/common'; import {NgOptimizedImage} from '@angular/common';
import {RouterLink} from '@angular/router'; import {RouterLink} from '@angular/router';
@@ -1,5 +1,5 @@
import {Component} from '@angular/core'; import {Component} from '@angular/core';
import {HeaderComponent} from "@header/header.component"; import {HeaderComponent} from "../header/header.component";
@Component({ @Component({
selector: 'app-commandlist', selector: 'app-commandlist',
@@ -0,0 +1,79 @@
<ng-container>
<app-header [current_page]="'community'" height="460px" background_image="/public/img/backgrounds/community.jpg"
[overlay_gradient]="0.5">
<div class="title" header-content>
<h1>Community</h1>
<h2>Talented people who help Altitude in more than one way.</h2>
</div>
</app-header>
<main>
<section class="darkmodeSection">
<div class="customContainer">
<h2>Current Nitro Boosters</h2>
</div>
</section>
<section id="social" class="darkmodeSectionThree">
<div class="container" style="padding: 50px 0 0 0; justify-content: center;">
<h2 class="sectionTitle">Social Media</h2>
</div>
<div style="display: flex; justify-content: center; padding-bottom: 30px;">
<p style="text-align: center;">We're currently not looking for more people to help manage our socials.</p>
</div>
</section>
<section id="crateTeam" class="darkmodeSection">
<div class="container" style="padding: 50px 0 0 0; justify-content: center;">
<h2 class="sectionTitle">Crate Team</h2>
</div>
</section>
<section class="darkmodeSectionThree">
<div class="container" style="padding: 50px 0 0 0; justify-content: center;">
<h2 class="sectionTitle">Event Leaders</h2>
</div>
<div style="display: flex; justify-content: center; padding-bottom: 30px;">
<p style="text-align: center;">We're currently not looking for more Event Leaders.</p>
</div>
</section>
<section class="darkmodeSection">
<div class="container" style="padding: 50px 0 0 0; justify-content: center;">
<h2 class="sectionTitle">Event Team</h2>
</div>
<div style="display: flex; justify-content: center; padding-bottom: 30px;">
<div style="flex-direction: column;">
<p style="text-align: center;">We occasionally open applications for the event team.</p>
<p style="text-align: center;">If you're interested in joining you simply need to keep an eye on the Discord
announcements.</p>
</div>
</div>
</section>
<section class="darkmodeSectionThree">
<div class="container" style="padding: 50px 0 0 0; justify-content: center;">
<h2 class="sectionTitle">YouTubers & Streamers</h2>
</div>
<div style="display: flex; justify-content: center; padding-bottom: 30px;">
<div style="flex-direction: column;">
<p style="text-align: center;"><a style="cursor: pointer;" id="reqButton">Show Requirements...</a></p>
</div>
</div>
<div id="req" class="hide" style="display: flex; justify-content: center; padding-bottom: 30px;">
<div style="flex-direction: column; justify-content: center; max-width: 800px;">
<p style="text-align: center;"><span style="font-family: 'opensans-bold', sans-serif;">Requirements:</span>
</p>
<p style="text-align: center;">You need to have at least one recent stream/video on Altitude which we can use
to gauge if your audience enjoys your content on Altitude.</p>
<br>
<p style="text-align: center;">Twitch: You need to be affiliate and get at least 5 viewers on average while
streaming on Altitude.</p>
<p style="text-align: center;">YouTube videos: You need at least 500 subs and have at least 200 views per
video within a week on average for Altitude content.</p>
<p style="text-align: center;">YouTube streamers: You need at least 500 subs and have at least 5 viewers on
average while streaming on Altitude.</p>
<br>
<p style="text-align: center;"><span style="font-family: 'opensans-bold', sans-serif;">Note:</span> Before
accepting or denying you we will watch your latest video/stream on Altitude (so keep your broadcasts public
on twitch).</p>
</div>
</div>
</section>
</main>
</ng-container>
@@ -0,0 +1,12 @@
.customContainer {
width: 80%;
max-width: 1020px;
margin: auto;
padding: 80px 0;
text-align: center;
}
.hide {
display: none !important;
}
@@ -1,18 +1,18 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { FramesComponent } from './frames.component'; import { CommunityComponent } from './community.component';
describe('FramesComponent', () => { describe('CommunityComponent', () => {
let component: FramesComponent; let component: CommunityComponent;
let fixture: ComponentFixture<FramesComponent>; let fixture: ComponentFixture<CommunityComponent>;
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
imports: [FramesComponent] imports: [CommunityComponent]
}) })
.compileComponents(); .compileComponents();
fixture = TestBed.createComponent(FramesComponent); fixture = TestBed.createComponent(CommunityComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();
}); });
@@ -0,0 +1,14 @@
import {Component} from '@angular/core';
import {HeaderComponent} from "../header/header.component";
@Component({
selector: 'app-community',
imports: [
HeaderComponent
],
templateUrl: './community.component.html',
styleUrl: './community.component.scss'
})
export class CommunityComponent {
}
@@ -1,5 +1,5 @@
import {Component} from '@angular/core'; import {Component} from '@angular/core';
import {HeaderComponent} from "@header/header.component"; import {HeaderComponent} from "../header/header.component";
import {RouterLink} from '@angular/router'; import {RouterLink} from '@angular/router';
@Component({ @Component({
@@ -1,5 +1,5 @@
import {Component} from '@angular/core'; import {Component} from '@angular/core';
import {HeaderComponent} from '@header/header.component'; import {HeaderComponent} from '../header/header.component';
import {NgOptimizedImage} from '@angular/common'; import {NgOptimizedImage} from '@angular/common';
@Component({ @Component({
@@ -1,5 +1,5 @@
import {Component} from '@angular/core'; import {Component} from '@angular/core';
import {ALTITUDE_VERSION} from '@custom-types/constant'; import {ALTITUDE_VERSION} from '../constant';
import {CommonModule, NgOptimizedImage} from '@angular/common'; import {CommonModule, NgOptimizedImage} from '@angular/common';
import {RouterLink} from '@angular/router'; import {RouterLink} from '@angular/router';
@@ -1,7 +1,7 @@
import {Component, OnInit} from '@angular/core'; import {Component, OnInit} from '@angular/core';
import {FormsComponent} from '../forms.component'; import {FormsComponent} from '../forms.component';
import {FormControl, FormGroup, Validators} from '@angular/forms'; import {FormControl, FormGroup, Validators} from '@angular/forms';
import {AppealsService, MinecraftAppeal} from '@api'; import {AppealsService, MinecraftAppeal} from '../../../api';
@Component({ @Component({
selector: 'app-appeal', selector: 'app-appeal',
@@ -1,12 +1,12 @@
import {Component, Input, OnInit} from '@angular/core'; import {Component, Input, OnInit} from '@angular/core';
import {HeaderComponent} from '@header/header.component'; import {HeaderComponent} from '../header/header.component';
import {MatDialog} from '@angular/material/dialog'; import {MatDialog} from '@angular/material/dialog';
import {ActivatedRoute} from '@angular/router'; import {ActivatedRoute} from '@angular/router';
import {LoginDialogComponent} from '@shared-components/login/login.component'; import {LoginDialogComponent} from '../login/login.component';
import {KeyValuePipe, NgForOf, NgIf} from '@angular/common'; import {KeyValuePipe, NgForOf, NgIf} from '@angular/common';
import {FormType} from './form_type'; import {FormType} from './form_type';
import {MatButton} from '@angular/material/button'; import {MatButton} from '@angular/material/button';
import {AuthService} from '@services/auth.service'; import {AuthService} from '../services/auth.service';
@Component({ @Component({
selector: 'app-forms', selector: 'app-forms',
@@ -1,7 +1,7 @@
import {Component} from '@angular/core'; import {Component} from '@angular/core';
import {HeaderComponent} from "@header/header.component"; import {HeaderComponent} from "../header/header.component";
import {RouterLink} from '@angular/router'; import {RouterLink} from '@angular/router';
import {ALTITUDE_VERSION} from '@custom-types/constant'; import {ALTITUDE_VERSION} from '../constant';
@Component({ @Component({
selector: 'app-guide', selector: 'app-guide',
@@ -1,6 +1,6 @@
import {Component, HostListener, Input} from '@angular/core'; import {Component, HostListener, Input} from '@angular/core';
import {CommonModule, NgOptimizedImage} from '@angular/common'; import {CommonModule, NgOptimizedImage} from '@angular/common';
import {ThemeComponent} from '@shared-components/theme/theme.component'; import {ThemeComponent} from '../theme/theme.component';
import {RouterLink} from '@angular/router'; import {RouterLink} from '@angular/router';
@Component({ @Component({
@@ -1,10 +1,10 @@
import {Component, OnInit} from '@angular/core'; import {Component, OnInit} from '@angular/core';
import {Title} from '@angular/platform-browser'; import {Title} from '@angular/platform-browser';
import {ALTITUDE_VERSION} from '@custom-types/constant'; import {ALTITUDE_VERSION} from '../constant';
import {ScrollService} from '@services/scroll.service'; import {ScrollService} from '../scroll/scroll.service';
import {CommonModule, NgOptimizedImage} from '@angular/common'; import {CommonModule, NgOptimizedImage} from '@angular/common';
import {HeaderComponent} from '@header/header.component'; import {HeaderComponent} from '../header/header.component';
import {CopyIpComponent} from '@shared-components/copy-ip/copy-ip.component'; import {CopyIpComponent} from '../copy-ip/copy-ip.component';
import {RouterLink} from '@angular/router'; import {RouterLink} from '@angular/router';
@Component({ @Component({
@@ -1,7 +1,7 @@
import {Component} from '@angular/core'; import {Component} from '@angular/core';
import {HeaderComponent} from "@header/header.component"; import {HeaderComponent} from "../header/header.component";
import {NgClass, NgOptimizedImage} from '@angular/common'; import {NgClass, NgOptimizedImage} from '@angular/common';
import {ScrollService} from '@services/scroll.service'; import {ScrollService} from '../scroll/scroll.service';
@Component({ @Component({
selector: 'app-lag', selector: 'app-lag',
@@ -6,7 +6,7 @@ import {MatInputModule} from '@angular/material/input';
import {MatFormFieldModule} from '@angular/material/form-field'; import {MatFormFieldModule} from '@angular/material/form-field';
import {NgIf} from '@angular/common'; import {NgIf} from '@angular/common';
import {MatSnackBar} from '@angular/material/snack-bar'; import {MatSnackBar} from '@angular/material/snack-bar';
import {AuthService} from '@services/auth.service'; import {AuthService} from '../services/auth.service';
@Component({ @Component({
selector: 'app-login', selector: 'app-login',
@@ -1,6 +1,6 @@
import {Component} from '@angular/core'; import {Component} from '@angular/core';
import {CommonModule} from '@angular/common'; import {CommonModule} from '@angular/common';
import {HeaderComponent} from '@header/header.component'; import {HeaderComponent} from '../header/header.component';
@Component({ @Component({
standalone: true, standalone: true,
@@ -1,5 +1,5 @@
import {Component} from '@angular/core'; import {Component} from '@angular/core';
import {HeaderComponent} from "@header/header.component"; import {HeaderComponent} from "../header/header.component";
@Component({ @Component({
selector: 'app-mapart', selector: 'app-mapart',

Some files were not shown because too many files have changed in this diff Show More