mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-03-21 09:54:36 -05:00
Docker setup for development (#2460)
This commit is contained in:
parent
cfb103800d
commit
baa4b43855
13
.env.example
13
.env.example
|
|
@ -10,12 +10,12 @@ DISCORD_CLIENT_SECRET=
|
|||
PATREON_ACCESS_TOKEN=
|
||||
|
||||
// Image upload
|
||||
STORAGE_END_POINT=
|
||||
STORAGE_ACCESS_KEY=
|
||||
STORAGE_SECRET=
|
||||
STORAGE_REGION=
|
||||
STORAGE_BUCKET=
|
||||
STORAGE_URL=
|
||||
STORAGE_END_POINT=http://127.0.0.1:9000
|
||||
STORAGE_ACCESS_KEY=minio-user
|
||||
STORAGE_SECRET=minio-password
|
||||
STORAGE_REGION=us-east-1
|
||||
STORAGE_BUCKET=sendou
|
||||
STORAGE_URL=http://127.0.0.1:9000
|
||||
|
||||
// Twitch integration for fetching streams
|
||||
TWITCH_CLIENT_ID=
|
||||
|
|
@ -23,6 +23,7 @@ TWITCH_CLIENT_SECRET=
|
|||
|
||||
SKALOP_SYSTEM_MESSAGE_URL=http://localhost:5900/system
|
||||
SKALOP_TOKEN=secret
|
||||
REDIS_URL=redis://redis:6379
|
||||
|
||||
VITE_SITE_DOMAIN=http://localhost:5173
|
||||
VITE_SKALOP_WS_URL=ws://localhost:5900
|
||||
|
|
|
|||
10
README.md
10
README.md
|
|
@ -90,6 +90,16 @@ You should then be able to access the application by visiting http://localhost:5
|
|||
|
||||
Use the admin panel at http://localhost:5173/admin to log in (impersonate) as the admin user "Sendou" or as a regular user "N-ZAP" as well as re-seed the database if needed.
|
||||
|
||||
#### Docker
|
||||
|
||||
Optionally, if you want to develop image upload, real-time features or chat, you can use Docker to spin up the Skalop service and Minio for image hosting. You will need [Docker](https://www.docker.com/) up and running and then run the following command:
|
||||
|
||||
```
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
Minio admin UI to manage uploaded photos should be up and running at http://localhost:9001
|
||||
|
||||
## Contributing
|
||||
|
||||
- **Developers**: Read [CONTRIBUTING.md](./CONTRIBUTING.md)
|
||||
|
|
|
|||
|
|
@ -1616,25 +1616,12 @@ const detailedTeam = (seedVariation?: SeedVariation | null) => () => {
|
|||
sql
|
||||
.prepare(
|
||||
/* sql */ `
|
||||
insert into "UnvalidatedUserSubmittedImage" ("validatedAt", "url", "submitterUserId")
|
||||
values
|
||||
(1672587342, 'AiGSM5T-cxm6BFGT7N_lA-1673297699133.webp', ${ADMIN_ID}),
|
||||
(1672587342, 'jTbWd95klxU2MzGFIdi1c-1673297932788.webp', ${ADMIN_ID})
|
||||
`,
|
||||
)
|
||||
.run();
|
||||
|
||||
sql
|
||||
.prepare(
|
||||
/* sql */ `
|
||||
insert into "AllTeam" ("name", "customUrl", "inviteCode", "bio", "avatarImgId", "bannerImgId")
|
||||
insert into "AllTeam" ("name", "customUrl", "inviteCode", "bio")
|
||||
values (
|
||||
'Alliance Rogue',
|
||||
'alliance-rogue',
|
||||
'${shortNanoid()}',
|
||||
'${faker.lorem.paragraph()}',
|
||||
1,
|
||||
2
|
||||
'${faker.lorem.paragraph()}'
|
||||
)
|
||||
`,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { cors } from "remix-utils/cors";
|
|||
import { z } from "zod/v4";
|
||||
import { db } from "~/db/sql";
|
||||
import { notFoundIfFalsy, parseParams } from "~/utils/remix.server";
|
||||
import { userSubmittedImage } from "~/utils/urls";
|
||||
import { userSubmittedImage } from "~/utils/urls-img";
|
||||
import { id } from "~/utils/zod";
|
||||
import {
|
||||
handleOptionsRequest,
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import i18next from "~/modules/i18n/i18next.server";
|
|||
import { nullifyingAvg } from "~/utils/arrays";
|
||||
import { databaseTimestampToDate } from "~/utils/dates";
|
||||
import { parseParams } from "~/utils/remix.server";
|
||||
import { userSubmittedImage } from "~/utils/urls";
|
||||
import { userSubmittedImage } from "~/utils/urls-img";
|
||||
import { id } from "~/utils/zod";
|
||||
import {
|
||||
handleOptionsRequest,
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { db } from "~/db/sql";
|
|||
import { HACKY_resolvePicture } from "~/features/tournament/tournament-utils";
|
||||
import { databaseTimestampToDate } from "~/utils/dates";
|
||||
import { notFoundIfFalsy, parseParams } from "~/utils/remix.server";
|
||||
import { userSubmittedImage } from "~/utils/urls";
|
||||
import { userSubmittedImage } from "~/utils/urls-img";
|
||||
import { id } from "~/utils/zod";
|
||||
import {
|
||||
handleOptionsRequest,
|
||||
|
|
|
|||
|
|
@ -15,13 +15,8 @@ import { useIsMounted } from "~/hooks/useIsMounted";
|
|||
import { usePagination } from "~/hooks/usePagination";
|
||||
import { useSearchParamState } from "~/hooks/useSearchParamState";
|
||||
import { databaseTimestampToDate } from "~/utils/dates";
|
||||
import {
|
||||
artPage,
|
||||
conditionalUserSubmittedImage,
|
||||
newArtPage,
|
||||
userArtPage,
|
||||
userPage,
|
||||
} from "~/utils/urls";
|
||||
import { artPage, newArtPage, userArtPage, userPage } from "~/utils/urls";
|
||||
import { conditionalUserSubmittedImage } from "~/utils/urls-img";
|
||||
import { ResponsiveMasonry } from "../../../modules/responsive-masonry/components/ResponsiveMasonry";
|
||||
import { ART_PER_PAGE } from "../art-constants";
|
||||
import type { ListedArt } from "../art-types";
|
||||
|
|
|
|||
|
|
@ -17,11 +17,8 @@ import { useHasRole } from "~/modules/permissions/hooks";
|
|||
import invariant from "~/utils/invariant";
|
||||
import { logger } from "~/utils/logger";
|
||||
import type { SendouRouteHandle } from "~/utils/remix.server";
|
||||
import {
|
||||
artPage,
|
||||
conditionalUserSubmittedImage,
|
||||
navIconUrl,
|
||||
} from "~/utils/urls";
|
||||
import { artPage, navIconUrl } from "~/utils/urls";
|
||||
import { conditionalUserSubmittedImage } from "~/utils/urls-img";
|
||||
import { metaTitle } from "../../../utils/remix";
|
||||
import { action } from "../actions/art.new.server";
|
||||
import { ART } from "../art-constants";
|
||||
|
|
|
|||
|
|
@ -11,7 +11,8 @@ import { BadgeDisplay } from "~/features/badges/components/BadgeDisplay";
|
|||
import { HACKY_resolvePicture } from "~/features/tournament/tournament-utils";
|
||||
import { useIsMounted } from "~/hooks/useIsMounted";
|
||||
import { databaseTimestampToDate } from "~/utils/dates";
|
||||
import { navIconUrl, userSubmittedImage } from "~/utils/urls";
|
||||
import { navIconUrl } from "~/utils/urls";
|
||||
import { userSubmittedImage } from "~/utils/urls-img";
|
||||
import type { CalendarEvent, ShowcaseCalendarEvent } from "../calendar-types";
|
||||
import { Tags } from "./Tags";
|
||||
import styles from "./TournamentCard.module.css";
|
||||
|
|
|
|||
|
|
@ -32,7 +32,8 @@ import {
|
|||
import invariant from "~/utils/invariant";
|
||||
import type { SendouRouteHandle } from "~/utils/remix.server";
|
||||
import { pathnameFromPotentialURL } from "~/utils/strings";
|
||||
import { CREATING_TOURNAMENT_DOC_LINK, userSubmittedImage } from "~/utils/urls";
|
||||
import { CREATING_TOURNAMENT_DOC_LINK } from "~/utils/urls";
|
||||
import { userSubmittedImage } from "~/utils/urls-img";
|
||||
import {
|
||||
CALENDAR_EVENT,
|
||||
REG_CLOSES_AT_OPTIONS,
|
||||
|
|
|
|||
|
|
@ -7,12 +7,8 @@ import { cachedFullUserLeaderboard } from "~/features/leaderboards/core/leaderbo
|
|||
import * as LeaderboardRepository from "~/features/leaderboards/LeaderboardRepository.server";
|
||||
import * as Seasons from "~/features/mmr/core/Seasons";
|
||||
import { cache, IN_MILLISECONDS, ttl } from "~/utils/cache.server";
|
||||
import {
|
||||
discordAvatarUrl,
|
||||
teamPage,
|
||||
userPage,
|
||||
userSubmittedImage,
|
||||
} from "~/utils/urls";
|
||||
import { discordAvatarUrl, teamPage, userPage } from "~/utils/urls";
|
||||
import { userSubmittedImage } from "~/utils/urls-img";
|
||||
import * as ShowcaseTournaments from "../core/ShowcaseTournaments.server";
|
||||
|
||||
export const loader = async ({ request }: LoaderFunctionArgs) => {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { FormWithConfirm } from "~/components/FormWithConfirm";
|
|||
import { TrashIcon } from "~/components/icons/Trash";
|
||||
import { Main } from "~/components/Main";
|
||||
import { SubmitButton } from "~/components/SubmitButton";
|
||||
import { userSubmittedImage } from "~/utils/urls";
|
||||
import { userSubmittedImage } from "~/utils/urls-img";
|
||||
|
||||
import { action } from "../actions/upload.admin.server";
|
||||
import { loader } from "../loaders/upload.admin.server";
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ import {
|
|||
topSearchPlayerPage,
|
||||
userPage,
|
||||
userSeasonsPage,
|
||||
userSubmittedImage,
|
||||
} from "~/utils/urls";
|
||||
import { userSubmittedImage } from "~/utils/urls-img";
|
||||
import { InfoPopover } from "../../../components/InfoPopover";
|
||||
import { TopTenPlayer } from "../components/TopTenPlayer";
|
||||
import {
|
||||
|
|
|
|||
|
|
@ -17,12 +17,8 @@ import type { TieredSkill } from "~/features/mmr/tiered.server";
|
|||
import { useIsMounted } from "~/hooks/useIsMounted";
|
||||
import { useHasRole } from "~/modules/permissions/hooks";
|
||||
import { databaseTimestampToDate } from "~/utils/dates";
|
||||
import {
|
||||
lfgNewPostPage,
|
||||
navIconUrl,
|
||||
userPage,
|
||||
userSubmittedImage,
|
||||
} from "~/utils/urls";
|
||||
import { lfgNewPostPage, navIconUrl, userPage } from "~/utils/urls";
|
||||
import { userSubmittedImage } from "~/utils/urls-img";
|
||||
import { hourDifferenceBetweenTimezones } from "../core/timezone";
|
||||
import type { LFGLoaderData, TiersMap } from "../routes/lfg";
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import { cancelScrimSchema } from "~/features/scrims/scrims-schemas";
|
|||
import { resolveRoomPass } from "~/features/tournament-bracket/tournament-bracket-utils";
|
||||
import { useHasPermission } from "~/modules/permissions/hooks";
|
||||
import type { SendouRouteHandle } from "~/utils/remix.server";
|
||||
import { userSubmittedImage } from "~/utils/urls-img";
|
||||
import { Avatar } from "../../../components/Avatar";
|
||||
import { Main } from "../../../components/Main";
|
||||
import { databaseTimestampToDate } from "../../../utils/dates";
|
||||
|
|
@ -22,7 +23,6 @@ import {
|
|||
scrimsPage,
|
||||
teamPage,
|
||||
userPage,
|
||||
userSubmittedImage,
|
||||
} from "../../../utils/urls";
|
||||
import { ConnectedChat } from "../../chat/components/Chat";
|
||||
import { action } from "../actions/scrims.$id.server";
|
||||
|
|
|
|||
|
|
@ -33,8 +33,8 @@ import {
|
|||
scrimPage,
|
||||
scrimsPage,
|
||||
userPage,
|
||||
userSubmittedImage,
|
||||
} from "~/utils/urls";
|
||||
import { userSubmittedImage } from "~/utils/urls-img";
|
||||
import {
|
||||
SendouTab,
|
||||
SendouTabList,
|
||||
|
|
|
|||
|
|
@ -66,8 +66,8 @@ import {
|
|||
sendouQMatchPage,
|
||||
specialWeaponImageUrl,
|
||||
teamPage,
|
||||
userSubmittedImage,
|
||||
} from "~/utils/urls";
|
||||
import { userSubmittedImage } from "~/utils/urls-img";
|
||||
import { action } from "../actions/q.match.$id.server";
|
||||
import { matchEndedAtIndex } from "../core/match";
|
||||
import { loader } from "../loaders/q.match.$id.server";
|
||||
|
|
|
|||
|
|
@ -9,12 +9,8 @@ import { Table } from "~/components/Table";
|
|||
import type { TeamResultsLoaderData } from "~/features/team/loaders/t.$customUrl.results.server";
|
||||
import { HACKY_resolvePicture } from "~/features/tournament/tournament-utils";
|
||||
import { databaseTimestampToDate } from "~/utils/dates";
|
||||
import {
|
||||
tournamentLogoUrl,
|
||||
tournamentTeamPage,
|
||||
userPage,
|
||||
userSubmittedImage,
|
||||
} from "~/utils/urls";
|
||||
import { tournamentLogoUrl, tournamentTeamPage, userPage } from "~/utils/urls";
|
||||
import { userSubmittedImage } from "~/utils/urls-img";
|
||||
|
||||
import styles from "./TeamResultsTable.module.css";
|
||||
|
||||
|
|
|
|||
|
|
@ -7,13 +7,8 @@ import { BskyIcon } from "~/components/icons/Bsky";
|
|||
import { Main } from "~/components/Main";
|
||||
import { metaTags } from "~/utils/remix";
|
||||
import type { SendouRouteHandle } from "~/utils/remix.server";
|
||||
import {
|
||||
bskyUrl,
|
||||
navIconUrl,
|
||||
TEAM_SEARCH_PAGE,
|
||||
teamPage,
|
||||
userSubmittedImage,
|
||||
} from "~/utils/urls";
|
||||
import { bskyUrl, navIconUrl, TEAM_SEARCH_PAGE, teamPage } from "~/utils/urls";
|
||||
import { userSubmittedImage } from "~/utils/urls-img";
|
||||
import { loader } from "../loaders/t.$customUrl.server";
|
||||
export { loader };
|
||||
|
||||
|
|
|
|||
|
|
@ -22,8 +22,8 @@ import {
|
|||
navIconUrl,
|
||||
TEAM_SEARCH_PAGE,
|
||||
teamPage,
|
||||
userSubmittedImage,
|
||||
} from "~/utils/urls";
|
||||
import { userSubmittedImage } from "~/utils/urls-img";
|
||||
import { action } from "../actions/t.server";
|
||||
import { loader } from "../loaders/t.server";
|
||||
import { TEAM, TEAMS_PER_PAGE } from "../team-constants";
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import {
|
|||
import invariant from "~/utils/invariant";
|
||||
import { logger } from "~/utils/logger";
|
||||
import { assertUnreachable } from "~/utils/types";
|
||||
import { userSubmittedImage } from "~/utils/urls";
|
||||
import { userSubmittedImage } from "~/utils/urls-img";
|
||||
import {
|
||||
fillWithNullTillPowerOfTwo,
|
||||
groupNumberToLetters,
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@ import { db } from "~/db/sql";
|
|||
import type { Tables, TablesInsertable } from "~/db/tables";
|
||||
import { databaseTimestampNow, dateToDatabaseTimestamp } from "~/utils/dates";
|
||||
import { COMMON_USER_FIELDS } from "~/utils/kysely.server";
|
||||
import { mySlugify, userSubmittedImage } from "~/utils/urls";
|
||||
import { mySlugify } from "~/utils/urls";
|
||||
import { userSubmittedImage } from "~/utils/urls-img";
|
||||
import { HACKY_resolvePicture } from "../tournament/tournament-utils";
|
||||
import { TOURNAMENT_SERIES_EVENTS_PER_PAGE } from "./tournament-organization-constants";
|
||||
|
||||
|
|
|
|||
|
|
@ -32,8 +32,8 @@ import {
|
|||
tournamentOrganizationPage,
|
||||
tournamentPage,
|
||||
userPage,
|
||||
userSubmittedImage,
|
||||
} from "~/utils/urls";
|
||||
import { userSubmittedImage } from "~/utils/urls-img";
|
||||
import { action } from "../actions/org.$slug.server";
|
||||
import { EventCalendar } from "../components/EventCalendar";
|
||||
import { SocialLinksList } from "../components/SocialLinksList";
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import { nullFilledArray, nullifyingAvg } from "~/utils/arrays";
|
|||
import { databaseTimestampNow, dateToDatabaseTimestamp } from "~/utils/dates";
|
||||
import { COMMON_USER_FIELDS, userChatNameColor } from "~/utils/kysely.server";
|
||||
import type { Unwrapped } from "~/utils/types";
|
||||
import { userSubmittedImage } from "~/utils/urls";
|
||||
import { userSubmittedImage } from "~/utils/urls-img";
|
||||
import { HACKY_resolvePicture } from "./tournament-utils";
|
||||
|
||||
export type FindById = NonNullable<Unwrapped<typeof findById>>;
|
||||
|
|
|
|||
|
|
@ -54,8 +54,8 @@ import {
|
|||
tournamentSubsPage,
|
||||
userEditProfilePage,
|
||||
userPage,
|
||||
userSubmittedImage,
|
||||
} from "~/utils/urls";
|
||||
import { userSubmittedImage } from "~/utils/urls-img";
|
||||
import { AlertIcon } from "../../../components/icons/Alert";
|
||||
import { action } from "../actions/to.$id.register.server";
|
||||
import type { TournamentRegisterPageLoader } from "../loaders/to.$id.register.server";
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@ import {
|
|||
tournamentMatchPage,
|
||||
tournamentTeamPage,
|
||||
userPage,
|
||||
userSubmittedImage,
|
||||
} from "~/utils/urls";
|
||||
import { userSubmittedImage } from "~/utils/urls-img";
|
||||
import { TeamWithRoster } from "../components/TeamWithRoster";
|
||||
import * as Standings from "../core/Standings";
|
||||
import type { PlayedSet } from "../core/sets.server";
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@ import {
|
|||
tournamentOrganizationPage,
|
||||
tournamentPage,
|
||||
tournamentRegisterPage,
|
||||
userSubmittedImage,
|
||||
} from "~/utils/urls";
|
||||
import { userSubmittedImage } from "~/utils/urls-img";
|
||||
import { metaTags } from "../../../utils/remix";
|
||||
|
||||
import { loader, type TournamentLoaderData } from "../loaders/to.$id.server";
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ import {
|
|||
tournamentLogoUrl,
|
||||
tournamentTeamPage,
|
||||
userPage,
|
||||
userSubmittedImage,
|
||||
} from "~/utils/urls";
|
||||
import { userSubmittedImage } from "~/utils/urls-img";
|
||||
import type { UserResultsLoaderData } from "../loaders/u.$identifier.results.server";
|
||||
import { ParticipationPill } from "./ParticipationPill";
|
||||
|
||||
|
|
|
|||
|
|
@ -224,7 +224,7 @@ function WeaponsSelector() {
|
|||
return newWeapons;
|
||||
})
|
||||
}
|
||||
initialValue={weapon ?? undefined}
|
||||
value={weapon ?? null}
|
||||
testId={`weapon-${i}`}
|
||||
/>
|
||||
{i === weapons.length - 1 && (
|
||||
|
|
|
|||
|
|
@ -23,8 +23,8 @@ import {
|
|||
navIconUrl,
|
||||
teamPage,
|
||||
topSearchPlayerPage,
|
||||
userSubmittedImage,
|
||||
} from "~/utils/urls";
|
||||
import { userSubmittedImage } from "~/utils/urls-img";
|
||||
import { loader } from "../loaders/u.$identifier.index.server";
|
||||
import type { UserPageLoaderData } from "../loaders/u.$identifier.server";
|
||||
export { loader };
|
||||
|
|
|
|||
|
|
@ -56,8 +56,8 @@ import {
|
|||
TIERS_PAGE,
|
||||
tournamentTeamPage,
|
||||
userSeasonsPage,
|
||||
userSubmittedImage,
|
||||
} from "~/utils/urls";
|
||||
import { userSubmittedImage } from "~/utils/urls-img";
|
||||
import {
|
||||
loader,
|
||||
type UserSeasonsPageLoaderData,
|
||||
|
|
|
|||
|
|
@ -320,7 +320,7 @@ function MatchesFieldset({
|
|||
key={i}
|
||||
isRequired
|
||||
testId={`player-${i}-weapon`}
|
||||
initialValue={value[i]}
|
||||
value={value[i] ?? null}
|
||||
onChange={(weaponId) => {
|
||||
const weapons = [...value];
|
||||
weapons[i] = weaponId;
|
||||
|
|
@ -343,7 +343,7 @@ function MatchesFieldset({
|
|||
key={i}
|
||||
isRequired
|
||||
testId={`player-${adjustedI}-weapon`}
|
||||
initialValue={value[adjustedI]}
|
||||
value={value[adjustedI] ?? null}
|
||||
onChange={(weaponId) => {
|
||||
const weapons = [...value];
|
||||
weapons[adjustedI] = weaponId;
|
||||
|
|
@ -361,7 +361,7 @@ function MatchesFieldset({
|
|||
label={t("vods:forms.title.weapon")}
|
||||
isRequired
|
||||
testId={`match-${idx}-weapon`}
|
||||
initialValue={value[0]}
|
||||
value={value[0] ?? null}
|
||||
onChange={(weaponId) => onChange([weaponId])}
|
||||
/>
|
||||
)}
|
||||
|
|
|
|||
15
app/utils/urls-img.ts
Normal file
15
app/utils/urls-img.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
// TODO: separating this file from urls.ts is a temporary solution. The reason is that import.meta.env cannot currently be used in files that are consumed by plain Node.js
|
||||
|
||||
const USER_SUBMITTED_IMAGE_ROOT =
|
||||
process.env.NODE_ENV === "development" &&
|
||||
import.meta.env.VITE_PROD_MODE !== "true"
|
||||
? "http://127.0.0.1:9000/sendou"
|
||||
: "https://sendou.nyc3.cdn.digitaloceanspaces.com";
|
||||
|
||||
// TODO: move development images to minio and deprecate this hack
|
||||
// images with https are not hosted on spaces, this is used for local development
|
||||
export const conditionalUserSubmittedImage = (fileName: string) =>
|
||||
fileName.includes("https") ? fileName : userSubmittedImage(fileName);
|
||||
|
||||
export const userSubmittedImage = (fileName: string) =>
|
||||
`${USER_SUBMITTED_IMAGE_ROOT}/${fileName}`;
|
||||
|
|
@ -53,14 +53,6 @@ export const BADGES_DOC_LINK =
|
|||
export const CREATING_TOURNAMENT_DOC_LINK =
|
||||
"https://github.com/sendou-ink/sendou.ink/blob/rewrite/docs/tournament-creation.md";
|
||||
|
||||
const USER_SUBMITTED_IMAGE_ROOT =
|
||||
"https://sendou.nyc3.cdn.digitaloceanspaces.com";
|
||||
export const userSubmittedImage = (fileName: string) =>
|
||||
`${USER_SUBMITTED_IMAGE_ROOT}/${fileName}`;
|
||||
// images with https are not hosted on spaces, this is used for local development
|
||||
export const conditionalUserSubmittedImage = (fileName: string) =>
|
||||
fileName.includes("https") ? fileName : userSubmittedImage(fileName);
|
||||
|
||||
export const PLUS_SERVER_DISCORD_URL = "https://discord.gg/FW4dKrY";
|
||||
export const SENDOU_INK_DISCORD_URL = "https://discord.gg/sendou";
|
||||
export const SENDOU_INK_PATREON_URL = "https://patreon.com/sendou";
|
||||
|
|
|
|||
41
compose.yaml
Normal file
41
compose.yaml
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
services:
|
||||
minio:
|
||||
image: "minio/minio:latest"
|
||||
ports:
|
||||
- "9000:9000"
|
||||
- "9001:9001"
|
||||
environment:
|
||||
MINIO_ROOT_USER: minio-user
|
||||
MINIO_ROOT_PASSWORD: minio-password
|
||||
entrypoint: >
|
||||
/bin/sh -c '
|
||||
isAlive() { curl -sf http://127.0.0.1:9000/minio/health/live; } # check if Minio is alive
|
||||
minio $$0 "$$@" --quiet & echo $$! > /tmp/minio.pid # start Minio in the background
|
||||
while ! isAlive; do sleep 0.1; done # wait until Minio is alive
|
||||
mc alias set minio http://127.0.0.1:9000 minio-user minio-password # setup Minio client
|
||||
mc mb minio/sendou || true # create a test bucket
|
||||
mc anonymous set public minio/sendou # make the test bucket public
|
||||
kill -s INT $$(cat /tmp/minio.pid) && rm /tmp/minio.pid # stop Minio
|
||||
while isAlive; do sleep 0.1; done # wait until Minio is stopped
|
||||
exec minio $$0 "$$@" # start Minio in the foreground
|
||||
'
|
||||
volumes:
|
||||
- ~/minio/data:/data
|
||||
command: server /data --console-address ":9001"
|
||||
|
||||
skalop:
|
||||
image: ghcr.io/sendou-ink/skalop:latest
|
||||
ports:
|
||||
- "5900:5900"
|
||||
environment:
|
||||
- REDIS_URL=${REDIS_URL}
|
||||
- SKALOP_TOKEN=${SKALOP_TOKEN}
|
||||
- SESSION_SECRET=${SESSION_SECRET}
|
||||
- PORT=5900
|
||||
depends_on:
|
||||
- redis
|
||||
restart: unless-stopped
|
||||
|
||||
redis:
|
||||
image: redis:alpine
|
||||
restart: unless-stopped
|
||||
|
|
@ -8,8 +8,13 @@ async function main() {
|
|||
// Step 1: Create .env if it doesn't exist
|
||||
if (!fs.existsSync(".env")) {
|
||||
logger.info("📄 .env not found. Creating from .env.example...");
|
||||
fs.copyFileSync(".env.example", ".env");
|
||||
logger.info(".env created with defaults values");
|
||||
const envContent = fs.readFileSync(".env.example", "utf-8");
|
||||
const filteredEnv = envContent
|
||||
.split("\n")
|
||||
.filter((line) => !line.trim().startsWith("//")) // remove comments to prevent issues with Docker
|
||||
.join("\n");
|
||||
fs.writeFileSync(".env", filteredEnv);
|
||||
logger.info(".env created with default values");
|
||||
}
|
||||
|
||||
const dbEmpty = !(await db.selectFrom("User").selectAll().executeTakeFirst());
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user