├── .gitpod.Dockerfile ├── .gitpod.yml ├── .vscode ├── settings.json └── wasp-0.1.0.vsix ├── LICENSE ├── MyNewApp ├── .gitignore ├── .waspignore ├── .wasproot ├── main.wasp ├── migrations │ ├── 20221214130100_init │ │ └── migration.sql │ └── migration_lock.toml └── src │ ├── client │ ├── LoginPage.jsx │ ├── Main.css │ ├── MainPage.jsx │ ├── SignupPage.jsx │ └── waspLogo.png │ ├── server │ ├── actions.js │ └── queries.js │ └── shared │ └── .gitkeep └── README.md /.gitpod.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:jammy 2 | 3 | RUN apt-get update && apt-get install -yq \ 4 | git \ 5 | git-lfs \ 6 | sudo \ 7 | curl 8 | 9 | # Gitpod expects us to have gitpod user with UID 333333, so let's create one. 10 | RUN useradd -l -u 33333 -G sudo -md /home/gitpod -s /bin/bash -p gitpod gitpod 11 | 12 | # Set up node (specifically version that Wasp requires). 13 | USER gitpod 14 | ENV NODE_VERSION=18.12.0 15 | RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash 16 | ENV NVM_DIR=/home/gitpod/.nvm 17 | RUN . "$NVM_DIR/nvm.sh" && nvm install ${NODE_VERSION} \ 18 | && nvm use v${NODE_VERSION} \ 19 | && nvm alias default v${NODE_VERSION} 20 | ENV PATH="/home/gitpod/.nvm/versions/node/v${NODE_VERSION}/bin:${PATH}" 21 | 22 | RUN curl -sSL https://get.wasp-lang.dev/installer.sh | sh -s 23 | # Wasp gets installed in $HOME/.local/bin, so we need to add it to PATH. 24 | ENV PATH="/home/gitpod/.local/bin:${PATH}" 25 | # Ensure Wasp's telemetry recognizes Wasp is running on Gitpod. 26 | RUN printf 'export WASP_TELEMETRY_CONTEXT=gitpod\n' >> $HOME/.bashrc \ 27 | && printf 'export WASP_TELEMETRY_USER_ID="%s"\n' "$GITPOD_WORKSPACE_ID" >> $HOME/.bashrc 28 | -------------------------------------------------------------------------------- /.gitpod.yml: -------------------------------------------------------------------------------- 1 | image: 2 | file: .gitpod.Dockerfile 3 | 4 | # Learn more about this file at https://www.gitpod.io/docs/references/gitpod-yml 5 | # We grab the dynamically created GITPOD_WORKSPACE_URL, add ports 3000/1, and create 6 | # the .env files necessary for Wasp Routing and CORS 7 | tasks: 8 | - command: | 9 | echo "REACT_APP_API_URL=$(printenv | grep "^GITPOD_WORKSPACE_URL=" | cut -d "=" -f 2 | awk '{gsub("https://","https://3001-")} 1')" >> MyNewApp/.env.client 10 | echo "WASP_WEB_CLIENT_URL=$(printenv | grep "^GITPOD_WORKSPACE_URL=" | cut -d "=" -f 2 | awk '{gsub("https://","https://3000-")} 1')" >> MyNewApp/.env.server 11 | cd MyNewApp 12 | wasp db migrate-dev 13 | wasp start 14 | 15 | ports: 16 | - port: 3000 17 | description: Web app 18 | onOpen: open-preview 19 | - port: 3001 20 | visibility: public 21 | description: Node.js server 22 | onOpen: ignore 23 | 24 | vscode: 25 | extensions: 26 | - wasp-lang.wasp 27 | - prisma.prisma -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "*.wasp": "wasp", 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /.vscode/wasp-0.1.0.vsix: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasp-lang/gitpod-template/b374c80fe4eb24318ec91c03d0a61be1c6bafdf7/.vscode/wasp-0.1.0.vsix -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Gitpod 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MyNewApp/.gitignore: -------------------------------------------------------------------------------- 1 | /.wasp/ 2 | /.env 3 | -------------------------------------------------------------------------------- /MyNewApp/.waspignore: -------------------------------------------------------------------------------- 1 | # Ignore editor tmp files 2 | **/*~ 3 | **/#*# 4 | -------------------------------------------------------------------------------- /MyNewApp/.wasproot: -------------------------------------------------------------------------------- 1 | File marking the root of Wasp project. -------------------------------------------------------------------------------- /MyNewApp/main.wasp: -------------------------------------------------------------------------------- 1 | app MyNewApp { 2 | 3 | wasp: { 4 | version: "^0.11.7" 5 | }, 6 | 7 | title: "MyNewApp", 8 | 9 | auth: { 10 | userEntity: User, 11 | methods: { 12 | // we also support Google OAuth2, with GitHub and more coming soon! 13 | usernameAndPassword: {}, 14 | }, 15 | onAuthFailedRedirectTo: "/login", 16 | } 17 | } 18 | 19 | // Use Prisma Schema Language (PSL) to define our entities: https://www.prisma.io/docs/concepts/components/prisma-schema 20 | // Run `wasp db migrate-dev` in the CLI to create the database tables 21 | // Then run `wasp db studio` to open Prisma Studio and view your db models 22 | entity User {=psl 23 | id Int @id @default(autoincrement()) 24 | username String @unique 25 | password String 26 | tasks Task[] 27 | psl=} 28 | 29 | entity Task {=psl 30 | id Int @id @default(autoincrement()) 31 | description String 32 | isDone Boolean @default(false) 33 | user User? @relation(fields: [userId], references: [id]) 34 | userId Int? 35 | psl=} 36 | 37 | route RootRoute { path: "/", to: MainPage } 38 | page MainPage { 39 | authRequired: true, // This page requires user to be authenticated. 40 | component: import Main from "@client/MainPage.jsx" 41 | } 42 | 43 | route LoginRoute { path: "/login", to: LoginPage } 44 | page LoginPage { 45 | component: import Login from "@client/LoginPage.jsx" 46 | } 47 | 48 | route SignupRoute { path: "/signup", to: SignupPage } 49 | page SignupPage { 50 | component: import Signup from "@client/SignupPage.jsx" 51 | } 52 | 53 | query getTasks { 54 | // We specify the JS implementation of our query (which is an async JS function) 55 | // which can be found in `server/queries.js` as a named export `getTasks`. 56 | fn: import { getTasks } from "@server/queries.js", 57 | // We tell Wasp that this query is doing something with the `Task` entity. With that, Wasp will 58 | // automatically refresh the results of this query when tasks change. 59 | entities: [Task] 60 | } 61 | 62 | action createTask { 63 | fn: import { createTask } from "@server/actions.js", 64 | entities: [Task] 65 | } 66 | 67 | action updateTask { 68 | fn: import { updateTask } from "@server/actions.js", 69 | entities: [Task] 70 | } -------------------------------------------------------------------------------- /MyNewApp/migrations/20221214130100_init/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateTable 2 | CREATE TABLE "User" ( 3 | "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, 4 | "username" TEXT NOT NULL, 5 | "password" TEXT NOT NULL 6 | ); 7 | 8 | -- CreateTable 9 | CREATE TABLE "Task" ( 10 | "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, 11 | "description" TEXT NOT NULL, 12 | "isDone" BOOLEAN NOT NULL DEFAULT false, 13 | "userId" INTEGER, 14 | CONSTRAINT "Task_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE SET NULL ON UPDATE CASCADE 15 | ); 16 | 17 | -- CreateIndex 18 | CREATE UNIQUE INDEX "User_username_key" ON "User"("username"); 19 | -------------------------------------------------------------------------------- /MyNewApp/migrations/migration_lock.toml: -------------------------------------------------------------------------------- 1 | # Please do not edit this file manually 2 | # It should be added in your version-control system (i.e. Git) 3 | provider = "sqlite" -------------------------------------------------------------------------------- /MyNewApp/src/client/LoginPage.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | import { LoginForm } from "@wasp/auth/forms/Login"; 4 | 5 | const LoginPage = () => { 6 | return ( 7 |
8 |

Login

9 | {/** Wasp has built-in auth forms & flows, which you can also opt-out of, if you wish :) */} 10 | 11 |
12 | 13 | I don't have an account yet (go to signup). 14 | 15 |
16 | ); 17 | }; 18 | 19 | export default LoginPage; 20 | -------------------------------------------------------------------------------- /MyNewApp/src/client/Main.css: -------------------------------------------------------------------------------- 1 | * { 2 | -webkit-font-smoothing: antialiased; 3 | -moz-osx-font-smoothing: grayscale; 4 | box-sizing: border-box; 5 | } 6 | 7 | main { 8 | padding: 1rem 0; 9 | flex: 1; 10 | display: flex; 11 | flex-direction: column; 12 | justify-content: center; 13 | align-items: center; 14 | } 15 | 16 | h1 { 17 | padding: 0; 18 | margin: 1rem 0; 19 | } 20 | 21 | main p { 22 | font-size: 1rem; 23 | } 24 | 25 | img { 26 | max-height: 100px; 27 | } 28 | 29 | button { 30 | margin-top: 1rem; 31 | } 32 | 33 | code { 34 | border-radius: 5px; 35 | padding: 0.2rem; 36 | background: #efefef; 37 | font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, 38 | Bitstream Vera Sans Mono, Courier New, monospace; 39 | } 40 | 41 | .auth-form h2 { 42 | margin-top: 0.5rem; 43 | font-size: 1.2rem; 44 | } 45 | 46 | .tasklist { 47 | display: flex; 48 | flex-direction: column; 49 | align-items: flex-start; 50 | justify-content: center; 51 | width: 300px; 52 | margin-top: 1rem; 53 | } 54 | -------------------------------------------------------------------------------- /MyNewApp/src/client/MainPage.jsx: -------------------------------------------------------------------------------- 1 | import './Main.css'; 2 | import React from 'react'; 3 | import logout from '@wasp/auth/logout'; 4 | import useAuth from '@wasp/auth/useAuth'; 5 | import { useQuery } from '@wasp/queries'; // Wasp uses a thin wrapper around react-query 6 | import getTasks from '@wasp/queries/getTasks'; 7 | import createTask from '@wasp/actions/createTask'; 8 | import updateTask from '@wasp/actions/updateTask'; 9 | import waspLogo from './waspLogo.png'; 10 | 11 | const MainPage = () => { 12 | const { data: tasks, isLoading, error } = useQuery(getTasks); 13 | const { data: user } = useAuth(); 14 | 15 | React.useEffect(() => { 16 | console.log(user); 17 | }, [user]); 18 | 19 | if (isLoading) return 'Loading...'; 20 | if (error) return 'Error: ' + error; 21 | 22 | return ( 23 |
24 | wasp logo 25 |

26 | {user.username} 27 | {`'s tasks :)`} 28 |

29 | 30 | {tasks && } 31 | 32 |
33 | ); 34 | }; 35 | 36 | const Task = (props) => { 37 | const handleIsDoneChange = async (event) => { 38 | try { 39 | await updateTask({ 40 | taskId: props.task.id, 41 | data: { isDone: event.target.checked }, 42 | }); 43 | } catch (error) { 44 | window.alert('Error while updating task: ' + error.message); 45 | } 46 | }; 47 | 48 | return ( 49 |
50 | 51 | {props.number + 1} 52 | {''} 53 | 54 | 55 | {props.task.description}{' '} 56 |
57 | ); 58 | }; 59 | 60 | const TasksList = (props) => { 61 | if (!props.tasks?.length) return 'No tasks'; 62 | 63 | return ( 64 |
65 | {props.tasks.map((task, idx) => )} 66 |
67 | ); 68 | }; 69 | 70 | const NewTaskForm = () => { 71 | const handleSubmit = async (event) => { 72 | event.preventDefault(); 73 | try { 74 | const description = event.target.description.value; 75 | event.target.reset(); 76 | await createTask({ description }); 77 | } catch (err) { 78 | window.alert('Error: ' + err.message); 79 | } 80 | }; 81 | 82 | return ( 83 |
84 | 85 | 86 |
87 | ); 88 | }; 89 | 90 | export default MainPage; 91 | -------------------------------------------------------------------------------- /MyNewApp/src/client/SignupPage.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | import { SignupForm } from "@wasp/auth/forms/Signup"; 4 | 5 | const SignupPage = () => { 6 | return ( 7 |
8 |

Sign Up

9 | {/** Wasp has built-in auth forms & flows, which you can also opt-out of, if you wish :) */} 10 | 11 |
12 | 13 | I already have an account (go to login). 14 | 15 |
16 | ); 17 | }; 18 | 19 | export default SignupPage; 20 | -------------------------------------------------------------------------------- /MyNewApp/src/client/waspLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasp-lang/gitpod-template/b374c80fe4eb24318ec91c03d0a61be1c6bafdf7/MyNewApp/src/client/waspLogo.png -------------------------------------------------------------------------------- /MyNewApp/src/server/actions.js: -------------------------------------------------------------------------------- 1 | import HttpError from '@wasp/core/HttpError.js'; 2 | 3 | export const createTask = async ({ description }, context) => { 4 | if (!context.user) { 5 | throw new HttpError(401); 6 | } 7 | return context.entities.Task.create({ 8 | data: { 9 | description, 10 | user: { connect: { id: context.user.id } }, 11 | }, 12 | }); 13 | }; 14 | 15 | export const updateTask = async ({ taskId, data }, context) => { 16 | if (!context.user) { 17 | throw new HttpError(401); 18 | } 19 | 20 | return context.entities.Task.updateMany({ 21 | where: { 22 | id: taskId, 23 | user: { id: context.user.id }, 24 | }, 25 | data: { isDone: data.isDone }, 26 | }); 27 | }; 28 | -------------------------------------------------------------------------------- /MyNewApp/src/server/queries.js: -------------------------------------------------------------------------------- 1 | import HttpError from '@wasp/core/HttpError.js'; 2 | 3 | export const getTasks = async (args, context) => { 4 | if (!context.user) { 5 | throw new HttpError(401); 6 | } 7 | return context.entities.Task.findMany({ where: { user: { id: context.user.id } } }); 8 | }; 9 | -------------------------------------------------------------------------------- /MyNewApp/src/shared/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasp-lang/gitpod-template/b374c80fe4eb24318ec91c03d0a61be1c6bafdf7/MyNewApp/src/shared/.gitkeep -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # A Wasp template on Gitpod 2 | 3 | [![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/wasp-lang/gitpod-template) 4 | 5 | 6 | 7 | This is a [Wasp](https://wasp-lang.dev/) template configured for ephemeral developer environments on [Gitpod](https://www.gitpod.io/). 8 | 9 | Inside this template is a simple ToDo app complete with Authentication, Database Entities, and Client & Server Operations. 10 | 11 | * Look for the `main.wasp` file within the `MyNewApp` directory to see how a Wasp App is built! 12 | * The `src` directory contains your client and server files that you edit and reference within `main.wasp` 13 | * Wasp uses this information to put all the pieces of your app together, so you can focus on the important stuff (you can see what Wasp builds in `.wasp/out`, but **don't** edit these files 🐝)! 14 | 15 | Visit [wasp-lang.dev](https://www.wasp-lang.dev) for more info. 16 | 17 | ## Next Steps 18 | 19 | After clicking the button above, you'll be taken to a new workspace. 20 | - Login with GitHub 21 | - Wait for the Docker image to build 22 | - Wait for the Wasp CLI to install and start the development environment 23 | - A preview of your app will open in a new tab within the IDE 24 | 25 | ## Get Started With Your Own Project 26 | 27 | ### A new project 28 | 29 | Click the above "Open in Gitpod" button to start a new workspace. Once you're ready to push your first code changes, Gitpod will guide you to fork this project so you own it. 30 | 31 | ### An existing project 32 | 33 | To get started with Wasp on Gitpod, add a [`.gitpod.yml`](./.gitpod.yml) file which contains the configuration to improve the developer experience on Gitpod. To learn more, please see the [Getting Started](https://www.gitpod.io/docs/getting-started) documentation. 34 | 35 | ## Wasp CLI Commands 36 | 37 | * `wasp start` starts development environment 38 | * `wasp db migrate-dev` to run database migrations 39 | * `wasp db studio` to open Prisma Studio and view your database models --------------------------------------------------------------------------------